diff --git a/ANNOUNCE.md b/ANNOUNCE.md
index bcbad55efc98..8ff85622f237 100644
--- a/ANNOUNCE.md
+++ b/ANNOUNCE.md
@@ -1,13 +1,12 @@
-The Wine development release 11.9 is now available.
+The Wine development release 11.10 is now available.
 
 What's new in this release:
-  - Bundled SQLite library.
-  - Initial support for system threads.
-  - Thread suspension in emulated code on ARM64.
+  - Bundled vkd3d upgraded to version 2.0.
+  - XPath support reimplemented without libxml2.
   - More VBScript compatibility improvements.
   - Various bug fixes.
 
-The source is available at <https://dl.winehq.org/wine/source/11.x/wine-11.9.tar.xz>
+The source is available at <https://dl.winehq.org/wine/source/11.x/wine-11.10.tar.xz>
 
 Binary packages for various distributions will be available
 from the respective [download sites][1].
@@ -19,292 +18,363 @@ See the file [AUTHORS][3] for the complete list.
 
 [1]: https://gitlab.winehq.org/wine/wine/-/wikis/Download
 [2]: https://gitlab.winehq.org/wine/wine/-/wikis/Documentation
-[3]: https://gitlab.winehq.org/wine/wine/-/raw/wine-11.9/AUTHORS
+[3]: https://gitlab.winehq.org/wine/wine/-/raw/wine-11.10/AUTHORS
 
 ----------------------------------------------------------------
 
-### Bugs fixed in 11.9 (total 24):
-
- - #36484  Lotus Notes 8.x installer aborts with SAX parser exception (line breaks not preserved)
- - #53317  Logos 9: Crash When Indexing: Invalid String Format
- - #53551  Unable to run a hello world console program: unhandled page fault
- - #53637  WinSCP UI rendering issue
- - #53877  vbscript compile_assignment assertion when assigning multidimensional array by indices
- - #54177  vbscript fails to compile sub call when argument expression contains multiplication
- - #56281  vbscript: String number converted to ascii value instead of parsed value
- - #57852  rebase (tool from the Windows SDK) crashes on unimplemented function imagehlp.dll.ReBaseImage64
- - #58125  Homesite 5.5 installer's progress bar misses its outline
- - #58925  GOM Player: UI elements not responding to mouse clicks
- - #59011  Wargaming Game Center Window not appearing / invisible
- - #59028  Graphpad Prism 9: project (.pzfx) files cannot be saved if msxml6 is not installed
- - #59409  msvcrt: scanf scanset character ranges with high bytes (\x80-\xff) broken due to signed char comparison in scanf.h
- - #59425  GXSCC crashes when a valid MIDI file is dragged in its window
- - #59611  ExamDiff Pro Fileeditor blocks on end of file
- - #59672  winhttp: DOAXVV fails with error 9003 at title screen
- - #59689  SEC_WINNT_AUTH_IDENTITY_EX support in AcquireCredentialsHandle
- - #59690  Both Command & Conquer 3 and Command & Conquer Red Alert 3 show the same kind of error
- - #59708  d3d9: missing MSVC vtable byte-pattern
- - #59718  d2d1: Three correctness issues in arc Bezier-approximation code (d2d_arc_to_bezier, d2d_figure_add_arc, d2d_arc_transform)
- - #59722  RemoveDirectoryW fails with ERROR_SHARING_VIOLATION after subprocess exit
- - #59729  Volumes do not report FILE_SUPPORTS_OPEN_BY_FILE_ID
- - #59743  regression: Wine crashes / segmentation fault's when starting Photoshop CS 2
- - #59746  SteelSeries GG 110.0 crashes on startup in .NET System.Security.Cryptography.X509Certificates.CertificateRequest..ctor
-
-### Changes since 11.8:
+### Bugs fixed in 11.10 (total 17):
+
+ - #9404   cannot select TWAIN source in irfanview
+ - #41047  Star Wars Racer videos have no sound
+ - #46635  git for windows works or crashes depending on how its invoked
+ - #48644  Kodak EasyShare installer doesn't populate list of countries (needs GEO_FRIENDLYNAME)
+ - #49720  Family Tree Maker 2017 installer fails after numerous "fixme" console messages
+ - #50589  Foxit PhantomPDF Business v10.0 installer fails, reports 'Printer installation failed, please check the print service.'
+ - #56482  Vocaloid 6 does not run
+ - #57329  Desktop files generated by Wine do not conform to the Desktop Entry Specification
+ - #58887  Opening screen of Photolemur 3 has 'tofu' characters between letters on three lines
+ - #58888  Photolemur 3 crashes if you use the Settings / Language menu
+ - #59522  explorerframe: ITaskbarList3 SetProgressValue/SetProgressState stubs - could use native desktop backends
+ - #59539  Querying namespace nodes via XPath's namespace:: axis returns NULL for all nodes. selectSingleNode("namespace::prefix") returns S_FALSE/NULL and selectNodes("namespace::*")   returns an empty list, even when the namespace is in scope.
+ - #59555  Star Wars: Knights of the Old Republic - Rendering failure with active framebuffer effects
+ - #59625  shcore: Set/GetCurrentProcessExplicitAppUserModelID are stubs
+ - #59771  TrackChecker-x64.exe hangs at 100% CPU on startup since Wine 11.7 (msxml3 regression)
+ - #59774  Age of Empires III: The Asian Dynasties crashes in the beginning cinematics
+ - #59789  comctl32:button: Test fails since 67898adb91
+
+### Changes since 11.9:
 ```
-Alexandre Julliard (36):
-      ntdll: Add a helper function to create a thread on the server side.
-      ntdll: Add a helper function to create a thread with pthread.
-      ntdll: Move single-step handling to the SIGTRAP handler.
-      ntdll: Don't allow getting/setting the context for threads without a TEB.
-      ntdll: Don't try to return from syscall for threads without a TEB.
-      ntdll: Handle signals in thread without a TEB.
-      ntdll: Don't raise an exception for threads without a TEB.
-      ntdll: Support last error functions in threads without a TEB.
-      ntdll: Pass the thread data explicitly in a few more functions.
-      ntdll: Store the initial suspend flag in the thread data.
-      ntdll: Move all the thread initialization into server_init_thread().
-      ntdll: Export PsCreateSystemThread.
-      server: Add support for system threads.
-      server: Don't return system threads in enumerations.
-      server: Don't allow accessing the context of system threads.
-      server: Don't send debug events for system threads.
-      server: Fix crash on invalid thread handle.
-      ntdll: Return explicit load order when checking version heuristics.
-      ntdll: Add builtin heuristics for Twain dll.
-      winemac: Use pthread instead of the TEB to access per-thread data.
-      winex11: Use pthread instead of the TEB to access per-thread data.
-      ntdll: Always get the 64-bit TEB from the 32-bit one.
-      ntdll: Pass the thread data to get_cpu_area().
-      ntdll: Add a helper function to initialize a CLIENT_ID structure.
-      winealsa.drv: Move zero_bits initialization to the wow64 attach callback.
-      winecoreaudio.drv: Move zero_bits initialization to the wow64 attach callback.
-      wineoss.drv: Move zero_bits initialization to the wow64 attach callback.
-      winepulse.drv: Move zero_bits initialization to the wow64 attach callback.
-      kernel32/tests: Adapt sort order test for Japanese and Korean.
-      kernel32/tests: Remove a sorting test that depends on Windows version.
-      kernel32/tests: Fix some geo name tests on latest Windows.
-      kernelbase: Don't store invalid geo names in the registry.
-      kernelbase: Implement GetGeoInfo(GEO_NAME).
-      kernelbase: Implement GetGeoInfo(GEO_RFC1766).
-      kernelbase: Implement GetGeoInfo(GEO_LCID).
-      kernelbase: Partially implement GetGeoInfo(GEO_FRIENDLYNAME).
-
-Alistair Leslie-Hughes (5):
-      propsys: Only trace Properties that cannot be found.
-      include: Add struct SHARDAPPIDINFOLINK.
-      include: Add JOB_STATUS_RESTART define.
-      include: Add define WC_NETADDRESS.
-      msado15: Correctly increase the cnt value.
-
-Aurimas Fišeras (1):
-      po: Update Lithuanian translation.
-
-Bernhard Übelacker (4):
-      windows.ui.core.textinput/tests: Move Release below check_interface.
-      kernelbase: Run conhost.exe from c:\windows\system32 directory.
-      ntdll: Use debugstr_wn instead of debugstr_w in nt_to_unix_file_name.
-      winex11.drv: Use hwnd_drop instead of hwnd in x11drv_dnd_drop_event.
-
-Brendan McGrath (2):
-      mf: Remove periodic callback on clock shutdown.
-      mf: Call GetCorrelatedTime without critical section.
-
-Brendan Shanks (3):
-      ntdll: Always synthesize ESR in SIGTRAP handler on ARM64 Linux.
-      ntdll/tests: Test unaligned access on ARM64.
-      ntdll: Add constants/helpers, and be more explicit when parsing the ARM64 ESR.
-
-Conor McCarthy (12):
-      mf/tests: Test optional topology nodes.
-      mf/topology_loader: Resolve topologies by walking down from each source node.
-      mf/topology_loader: Try to insert optional nodes after resolving the surrounding branch.
-      mf/tests: Add asynchronous transform tests.
-      mf/session: Support asynchronous transforms.
-      mf/tests: Test for preroll support in the sample grabber.
-      mf/tests: Test preroll notification when the rate has not been set.
-      mf/session: Initialise the stored presentation rate.
-      mf/scheme_handler: Call PathCreateFromUrlW() for URL to path conversion.
-      mfplat/tests: Create the new file in the temp path in test_file_stream().
-      mfplat/tests: Test leading forward slashes and colons in filename URLs.
-      mfplat: Normalise URLs before passing to BeginCreateObject().
-
-Dan Ginovker (1):
-      d3d9: Add a fake d3d9 device vtbl initialization sequence.
-
-Dmitry Timoshkov (4):
-      wineboot: Run services.exe from c:\windows\system32 directory.
-      adsldp: Add support for ADS_OPTION_REFERRALS.
-      crypt32: Use case-insensitive comparison for registered OID names.
-      adsldp: Add support for ADS_SEARCHPREF_CHASE_REFERRALS.
-
-Esme Povirk (2):
-      win32u: Support OBJID_CLASSNAMEIDX in scrollbar controls.
-      wbemprox: Stub Win32_DiskDrive TotalHeads and Signature properties.
-
-Francis De Brabandere (35):
-      vbscript/tests: Add coverage for BSTR vs numeric comparison.
-      vbscript/tests: Cover BSTR comparison with VT_BYREF and additional numeric VTs.
-      vbscript/tests: Cover BSTR comparison literal vs non-literal dispatch.
-      vbscript: Match native BSTR-vs-numeric and BSTR-vs-Boolean comparison.
-      vbscript/tests: Cover BSTR comparison with dispatch-only numeric VTs.
-      vbscript: Reject Automation types not supported in VBScript on compare.
-      vbscript/tests: Cover bare match.SubMatches(N) indexing.
-      vbscript: Implement DateValue and TimeValue functions.
-      vbscript/tests: Cover non-literal numeric vs whitespace-only and control-char BSTR.
-      vbscript: Treat all-whitespace and control-char BSTR as greater than numeric.
-      vbscript: Return subscript-out-of-range from UBound/LBound on uninitialized array.
-      vbscript: Return Null from Day/Month/Year/Hour/Minute/Second on Null operand.
-      vbscript: Handle Null operands in Left().
-      vbscript: Return the script class name from TypeName() on a class instance.
-      oleaut32: Fix Null handling in VarAnd.
-      oleaut32: Fix VT_CY Null handling in VarImp.
-      oleaut32: Fix VT_DATE Null handling in VarImp.
-      vbscript: Match native UI1 Imp Null behavior.
-      vbscript/tests: Add Null handling smoke tests for logical operators.
-      vbscript/tests: Cover Dim/Sub shadowing of host members.
-      vbscript: Coerce VT_EMPTY operands before Var* calls.
-      vbscript: Support assignment to chained array index expressions.
-      vbscript: Implement DateDiff built-in function.
-      vbscript/tests: Cover empty parens on properties, Dim variables, and arrays.
-      vbscript: Raise illegal-func-call on empty parens of class variant property.
-      vbscript: Delegate do_icall is_call branch to variant_call.
-      vbscript/tests: Cover top-level Const behavior.
-      vbscript/tests: Cover cross-parse name redefinition semantics.
-      vbscript: Match native cross-parse name redefinition rules.
-      vbscript: Implement IDispatch::GetTypeInfo for class instances via ICreateTypeLib2.
-      vbscript: Fix crash on fixed-size Dim array inside Execute called from a local scope.
-      vbscript/tests: Add tests for BSTR vs numeric/bool with neither side literal.
-      vbscript: Treat non-literal BSTR as greater than non-literal numeric/bool.
-      vbscript/tests: Add tests for &H8000 literal type.
-      vbscript: Parse &H8000 as Long(-32768) like native.
-
-Georg Lehmann (1):
-      winevulkan: Update to VK spec version 1.4.350.
-
-Giang Nguyen (3):
-      d2d1: Fix coordinate in arc-to-bezier conversion for degenerate case.
-      d2d1: Fix arc center translation in arc-to-bezier conversion.
-      d2d1: Do not translate radius vectors in arc-to-bezier conversion.
+Aaron Yourk (2):
+      ole32/tests: Add test for OLE clipboard reuse across STA threads.
+      ole32: Validate cached clipboard window handle in get_clipbrd_window.
+
+Alex Henrie (7):
+      wininet: Fix a memory leak in HTTP_DoAuthorization.
+      shell32: Correct folder rename icon ID.
+      shell32: Correct XP control panel icon ID.
+      shell32: Add computer-turning-off icon.
+      shell32: Correct shield icon ID.
+      winex11: Add missing const in langid_from_xkb_layout (GCC).
+      winebth: Fix a memory leak in bluez_gatt_characteristic_read.
+
+Alexandre Julliard (22):
+      win32u: Implement NtUserGetMessagePos().
+      win32u: Implement NtUserSetMessageExtraInfo().
+      mmdevapi: Use a system thread for the audio driver main loop thread.
+      mmdevapi: Use a system thread for the audio driver timer loop thread.
+      user32: Reimplement InSendMessageEx() using NtUserGetThreadState().
+      win32u: Remove redundant receive_flags from the user thread info.
+      user32: Use NtUserGetAncestor to check for desktop window.
+      win32u: Move desktop and message windows to the internal thread info.
+      win32u: Move the GetMessagePos/Time/ExtraInfo values to the internal thread info.
+      imm32: Get the default IMC from the win32u thread state.
+      win32u: Move the default IMC to the internal thread info.
+      win32u: Use a private counter for winproc user callbacks recursion.
+      win32u: Use pthread instead of the TEB to access internal per-thread data.
+      win32u: Avoid using NtUserGetThreadInfo() on the Unix side.
+      vkd3d: Import upstream release 2.0.
+      user32/tests: Add some more tests for GetCurrentInputMessageSource().
+      win32u: Save/restore the message source directly in call_window_proc().
+      win32u: Add support for threads that don't have a TEB.
+      uxtheme: Don't repaint the window itself in DrawThemeParentBackground.
+      include: Move some definitions to processthreadsapi.h.
+      include: Add a number of Interlocked intrinsic defines.
+      nls: Update sorting weights to the Windows 11 version.
+
+Alistair Leslie-Hughes (14):
+      uiautomationcore: Add UiaDisconnectAllProviders stub.
+      include: Add ndattrib.idl.
+      include: Add ndfapi.h.
+      hnetcfg: Return empty collection in INetFwRules_NewEnum.
+      ndfapi: Add a stub dll.
+      wkscli: Add stub dll.
+      wkscli: Move some functions from netapi32.
+      include: Rename tpcshrd.h to tpcshrd.idl.
+      include: Add rtscom.idl.
+      advapi32: Add WmiCloseBlock stub.
+      thumbcache: Add stub dll.
+      uuid: Add thumbcache.h.
+      thumbcache: Add LocalThumbnailCache stubbed interface.
+      sechost: Skip spaces after a bracket when parsing acl strings.
+
+Andrey Gusev (1):
+      qcap: Change helpstring for AVICo coclass.
+
+Aric Stewart (1):
+      uniscribe: Correct possible buffer over/under runs.
+
+Arie Miller (1):
+      shell32: Don't add backslash after CSIDL_PERSONAL.
+
+Bartosz Kosiorek (6):
+      msvcp90: Add _FSinh implementation.
+      msvcp90: Add _Sinh implementation.
+      msvcp90: Add _FCosh implementation.
+      msvcp90: Add _Cosh implementation.
+      msvcp90: Add _LCosh implementation.
+      msvcp90: Add _LSinh implementation.
+
+Benjamin Fischer (1):
+      msvcrt: Avoid expensive timezone queries from plain time() calls.
+
+Bernhard Kölbl (1):
+      wine.inf: Add ImmersiveShell registry key.
+
+Bernhard Übelacker (3):
+      regedit: Remove unused variable.
+      uninstaller: Remove unused variable.
+      setupapi: Only copy dest when it got written by SetupCopyOEMInfW (ASan).
+
+Bohdan Tkachenko (2):
+      sapi: Implement ISpVoice::GetStatus.
+      sapi: Implement ISpVoice::SetPriority and GetPriority.
+
+Brendan Shanks (1):
+      ntdll: Handle SIGBUS with the SIGSEGV handler on ARM64.
+
+Connor McAdams (14):
+      d3dx10/tests: Add tests for loading 2D texture array DDS files.
+      d3dx11/tests: Add tests for loading 2D texture array DDS files.
+      d3dx10: Add support for loading DDS files containing texture arrays.
+      d3dx11: Add support for loading DDS files containing texture arrays.
+      d3dx10/tests: Add tests for D3DX10LoadTextureFromTexture().
+      d3dx10: Validate some input arguments in D3DX10LoadTextureFromTexture().
+      d3dx10: Handle cases in D3DX10LoadTextureFromTexture() where no data is loaded.
+      d3dx10: Implement texture level loading in D3DX10LoadTextureFromTexture().
+      d3dx10: Add support for mipmap generation in D3DX10LoadTextureFromTexture().
+      d3dx10: Add support for 3D textures in D3DX10LoadTextureFromTexture().
+      user32/tests: Add tests for RealGetWindowClass.
+      comctl32/tests: Add tests for RealGetWindowClass.
+      win32u/tests: Add a test for real window class name retrieval.
+      user32: Pass real argument to NtUserGetClassName in RealGetWindowClass{A/W}.
+
+Elizabeth Figura (33):
+      Revert "packager: Prefer native version."
+      Revert "dxcore: Prefer native."
+      vulkan-1: Do not prefer native.
+      ksuser: No longer prefer native.
+      qdvd: No longer prefer native.
+      wmvcore: No longer prefer native.
+      wmp: No longer prefer native.
+      d3dcompiler: No longer prefer native.
+      d3dx9: No longer prefer native.
+      d3dx10: No longer prefer native.
+      d3dx11: No longer prefer native.
+      bluetoothapis: No longer prefer native.
+      dcomp: No longer prefer native.
+      dxva2: No longer prefer native.
+      fltlib: No longer prefer native.
+      pdh: No longer prefer native.
+      virtdisk: No longer prefer native.
+      winsta: No longer prefer native.
+      sfc: No longer prefer native.
+      sfc_os: No longer prefer native.
+      sas: No longer prefer native.
+      fwpuclnt: No longer prefer native.
+      d3dim: No longer prefer native.
+      d3dim700: No longer prefer native.
+      tdh: No longer prefer native.
+      rtutils: No longer prefer native.
+      quartz/tests: Use %I64.
+      quartz: Use %I64.
+      qasf/tests: Use %I64.
+      qcap/tests: Use %I64.
+      qedit/tests: Use %I64.
+      amstream/tests: Use %I64.
+      amstream: Use %I64.
+
+Esme Povirk (1):
+      configure: Document the other --enable-archs values.
+
+Feifan He (1):
+      winemac: Handle reset parameter in ClipCursor.
+
+Francis De Brabandere (21):
+      vbscript/tests: Add tests for ExecuteGlobal re-Dim of an existing array.
+      vbscript: Reject re-Dim of an existing global array with err 13.
+      vbscript/tests: Add tests for Cxxx coercion of Null and Nothing.
+      vbscript: Raise err 94 on Cxxx(Null) and err 91 on Cxxx(Nothing).
+      vbscript/tests: Add tests for And/Or/Imp with BSTR operands.
+      vbscript: Coerce BSTR operands of And/Or/Imp to number-or-Boolean.
+      vbscript/tests: Add tests for ReDim on class member arrays.
+      vbscript: Mark class member fixed-size arrays as FADF_FIXEDSIZE.
+      vbscript/tests: Add tests for UBound/LBound on non-array arguments.
+      vbscript: Match native error codes for non-array arguments to UBound/LBound.
+      vbscript: Parse octal numeric literals (&O...).
+      vbscript: Parse double literals via ucrtbase _wcstod_l.
+      vbscript/tests: Add tests for declaration scope in redefinition checks.
+      vbscript: Check top-level name redefinition against global scope only.
+      scrrun/tests: Add test for hashing failure of a degenerate object key.
+      scrrun/dictionary: Report key-hashing failures from lookups.
+      vbscript/tests: Add tests for error reporting from a GetRef reference.
+      vbscript: Run a script-invoked function reference as an internal call.
+      vbscript/tests: Note the error HRESULT seen by an external caller.
+      vbscript/tests: Add tests for class members sharing a global Dim/Const name.
+      vbscript: Compile class members in a separate namespace from globals.
 
 Hans Leidekker (3):
-      winhttp: Don't buffer more than one chunk.
-      winhttp: Always queue async writes.
-      msv1_0: Handle SEC_WINNT_AUTH_IDENTITY_EX.
-
-Ivan Ivlev (3):
-      winmm/tests: Use non-static struct to avoid clang compilation errors.
-      vccorlib140/tests: Use non-static objects to avoid clang compilation errors.
-      dmsynth/tests: Use macro instead of static function to avoid clang compilation errors.
-
-Jacek Caban (25):
-      advapi32/tests: Remove unused variable.
-      kernel32/tests: Avoid unused variables.
-      server: Store the client page size in the process struct.
-      ntdll/tests: Avoid unused global variables in exception.c.
-      ntdll/tests: Avoid unused global variables in wow64.c.
-      ntdll/tests: Avoid unused global variables.
-      gitlab: Update to llvm-mingw 20260505.
-      msvcrt: Provide trunc in the import library.
-      keyboard.drv: Remove unused global variables.
-      mouse.drv: Remove unused global variable.
-      win87em: Remove unused global variables.
-      comctl32/tests: Avoid unused global variables.
-      localui/tests: Remove unused global variables.
-      secur32/tests: Directly use secur32 functions in main.c.
-      kerberos: Remove unused variable.
-      msv1_0: Remove unused variables.
-      kernel32/tests: Avoid unused global variables on 32-bit ARM target.
-      mountmgr.drv: Avoid unused global variable.
-      ntdll: Avoid unused global variables.
-      ntdll/tests: Avoid unused global variables in wow64.c.
-      ntoskrnl/tests: Remove unused variable.
-      windows.media.speech/tests: Remove unused variables.
-      ntdll: Add support for ARM64EC cooperative suspend.
-      ntdll: Use unwind_context values only if the caller is not EC in capture_context.
-      ntdll/tests: Add SuspendDoorbell tests.
-
-Joel Holdsworth (4):
-      ntdll/tests: Add test for FILE_SUPPORTS_OPEN_BY_FILE_ID in FileFsAttributeInformation.
-      ntdll: Report FILE_SUPPORTS_OPEN_BY_FILE_ID.
-      kernel32/tests: Add test for FILE_SUPPORTS_OPEN_BY_FILE_ID on volume handles.
-      mountmgr: Report FILE_SUPPORTS_OPEN_BY_FILE_ID.
+      include: Update CERT_KEY_CONTEXT.
+      msctf: Add a stub implementation of ITfFnReconversion.
+      odbc32: Add support for driver connection attributes.
+
+Henri Verbeet (1):
+      wined3d: Remove the "more than one back buffer" FIXME from wined3d_swapchain_init().
+
+Huw D. M. Davies (1):
+      hal: Fix #endif guard comment.
+
+Ivan Ivlev (1):
+      oleaut32: Initialize page_info in OleCreatePropertyFrameIndirect.
+
+Jacek Caban (4):
+      ntdll: Don't hold the InSyscallCallback flag during the NtReadFile syscall.
+      ntdll: Use cooperative suspend for syscall callbacks.
+      ntdll/tests: Add tests for suspending ARM64EC syscall callbacks.
+      ntdll: Allow calling NtContinue in syscall callbacks.
+
+Joerg Rueppel (1):
+      d3dxof: Pad with magic value before object data in parse_object().
 
 Jon Koops (2):
-      dsound/tests: Test primary buffer independence across IDirectSound objects.
-      dsound: Create independent devices for each DirectSoundCreate call.
-
-Luca Bacci (1):
-      msvcrt: Set errno in failure path.
-
-Matteo Bruni (3):
-      winmm: Fix voicecom IMMDevice leak in device enumeration.
-      winmm/tests: Add some DRVM_MAPPER_CONSOLEVOICECOM_GET tests.
-      winmm/tests: Check that the preferred device is always at index 0.
-
-Mykola Mykhno (2):
-      cmd: Update tests for parentheses in quoted parameter.
-      cmd: Remove parentheses from list of illegal characters for quote strip.
-
-Nikolay Sivov (7):
-      msxml3: Fix missing document output in save().
-      msxml3/tests: Add some tests for cloning textual nodes.
-      msxml3: Clone textual contents when cloning nodes.
-      msxml3: Handle baseName property for entity references.
-      msxml3/tests: Remove some refcount tests.
-      msxml3/tests: Add a basic test for replaceChild() on attributes.
-      msxml3/tests: Add some tests for selecting from detached nodes.
-
-Paul Gofman (16):
-      wininet/tests: Add a test for multiple server addresses.
-      wininet: Pass proxy server addr_str if present in open_http_connection().
-      wininet: Store server address related data in the new server_addr_t structure.
-      wininet: Enclose ipv6 address string in square brackets.
-      wininet: Introduce and use create_connect_socket().
-      wininet: Try to connect to multiple server addresses.
-      wininet: Do not force ipv4 addresses.
-      ws2_32/tests: Properly check for loopback address in test_gethostbyname().
-      ws2_32: Exclude not found and loopback addresses in get_local_ips() if others present.
-      win32u: Bump D3DKMT driver version.
-      ddraw/tests: Don't leak window and ddraw in test_d3d_state_reset() on ddraw2.
-      ddraw/tests: Add tests for clipper in exclusive fullscreen mode.
-      ddraw: Do not render to the clipping window in exclusive fullscreen mode.
-      winsqlite3: Import sqlite3 from upstream release 3.51.1.
-      include: Add winsqlite3.h.
-      winsqlite3/tests: Add a basic test.
-
-Piotr Pawłowski (2):
-      comctl32/tests: Print id field of logged/expected messages on failure.
-      uxtheme/tests: Print id field of logged/expected messages on failure.
-
-Rémi Bernon (5):
-      mf/topology_loader: Get rid of the topoloader_context.
-      mf/topology_loader: Clone and validate topology in a dedicated helper.
-      mf/topology_loader: Copy branches with cloned nodes before connecting.
-      mf/topology_loader: Add some sanity checks for optional output nodes.
-      mf/topology_loader: Force enumerate types when optional node method is set.
-
-Sameer Singh (समीर सिंह) (2):
-      gdi32/uniscribe: Perform bounds check in insert_glyph().
-      gdi32/uniscribe: Resize the buffers if glyph count exceeds limit.
-
-Sven Baars (1):
-      wine: Show all dlopen() error messages instead of just the last one.
-
-Twaik Yont (5):
-      wineandroid: Add reply_fd plumbing to ioctl path.
-      wineandroid: Switch ioctl dispatch to socket transport on main thread looper.
-      wineandroid: Switch dequeueBuffer to socket transport.
-      wineandroid: Remove unixlib/ntoskrnl ioctl path.
-      wineandroid: Return ioctl errors directly.
-
-Yuxuan Shui (1):
-      preloader: Account for ld.so stack usage when reserving.
-
-Zakaria Habri (1):
-      winewayland: Use wp_pointer_warp_v1 for SetCursorPos when available.
-
-Zhiyi Zhang (2):
-      include: Add D2D1_TURBULENCE_NOISE.
-      include: Add IDCompositionDevice3.
+      dsound: Get rid of DirectSoundDevice reference counting.
+      dsound: Remove unused DSOUND_renderers list and lock.
+
+Kun Yang (2):
+      kernel32/tests: Add a test for ReplaceFileW.
+      kernelbase: Fail in ReplaceFileW when replaced file is a directory.
+
+Marc-Aurel Zent (2):
+      winemac: Set IME open status depending on HKL.
+      imm32: Fix ime_ui_notify() TRACE log.
+
+Matteo Bruni (6):
+      win32u: Tweak a couple of comments.
+      win32u: Simplify layout name generation for base keyboard layouts.
+      winex11: Keep a list of every known Xkb keyboard layout.
+      winex11: Store Xkb keyboard layout KLID.
+      winex11: Store Xkb keyboard layout id.
+      dsound: Get rid of the all_stopped argument.
+
+Matthias Gorzellik (1):
+      kernelbase: Add null check for str in OutputDebugStringW.
+
+Nikolay Sivov (10):
+      msxml3: Add a root element helper.
+      msxml3: Add a helper to get last child node.
+      msxml3: Expose previous sibling helper.
+      msxml3: Mark namespace declaration nodes on creation.
+      msxml3: Add implicit 'xml' namespace as document attribute.
+      msxml3: Reimplement XPath support without libxml2.
+      msxml3: Properly free parsed selection namespaces (ASan).
+      msxml3/sax: Fix UTF-16 BE decoder.
+      msxml3/sax: Remove commented out code.
+      oleaut32/tests: Use correct symbol for the Seek() argument.
+
+Paul Gofman (4):
+      ws2_32/tests: Test select() with valid but non-socket handle.
+      ws2_32: Return WSAENOTSOCK instead of WSAENOTSUPP from select() when trying to select on non-socket handle.
+      wined3d: Handle data aliasing when blitting to itself with FX transforms.
+      msctf: Return stub interface from ThreadMgr_GetFunctionProvider().
+
+Piotr Caban (12):
+      uuid: Add IID_IEnterActivityWithNoLock.
+      uuid: Add IID_ICallbackWithNoReentrancyToApplicationSTA.
+      combase: Share context token between MTA threads.
+      combase: Add IComThreadingInfo::SetCurrentLogicalThreadId implementation.
+      ole32: Add IRundown interface definition.
+      combase: Add IRundown interface stub.
+      combase: Implement IContextCallback::ContextCallback.
+      ole32: Add IContextCallback::ContextCallback tests.
+      combase: Enable remoting for STA threads in CoWaitForMultipleHandles.
+      wbemprox: Fix Win32_OperatingSystem.TotalVirtualMemorySize value.
+      wbemprox: Fix Win32_OperatingSystem.FreeVirtualMemory value.
+      msxml3: Fallback to UTF-8 encoding if detection fails.
+
+Robert Gerigk (8):
+      ntoskrnl: Store DEVPKEY_Device_Parent when enumerating child devices.
+      ntoskrnl/tests: Add DEVPKEY_Device_Parent test.
+      ntoskrnl: Store DEVPKEY_Device_Children on parent after bus enumeration.
+      ntoskrnl/tests: Add DEVPKEY_Device_Children test.
+      ntoskrnl: Store DEVPKEY_Device_Siblings on each child after bus enumeration.
+      ntoskrnl/tests: Add DEVPKEY_Device_Siblings test.
+      cfgmgr32: Implement CM_Get_Parent using DEVPKEY_Device_Parent property.
+      ntoskrnl/tests: Test CM_Get_Parent on the PnP-managed child device.
+
+Rose Hellsing (2):
+      riched20: Implement EM_LINELENGTH with wParam==-1.
+      riched20/tests: Add EM_LINELENGTH(-1) tests.
+
+Rémi Bernon (22):
+      win32u: Handle menu name ansi conversion on the PE side.
+      win32u: Simplify window class menu name management.
+      win32u: Avoid leaking user lock on NtUserGetClassInfoEx error.
+      win32u: Simplify internal small class icon creation.
+      win32u: Allow OpenGL context / HDC pixel format mismatch.
+      server: Move shared memory initialization out of create_class.
+      server: Introduce a struct class_info to wrap window class info.
+      server: Move class window proc to the shared memory.
+      win32u: Move class cursor and background to shared memory.
+      win32u: Move window class icons to the shared memory.
+      win32u: Move window class menu names to the shared memory.
+      win32u: Keep track of the registered builtin class atoms.
+      win32u: Create FNID from the ntuser_client_procs enum.
+      win32u: Get rid of unnecessary private_off window field.
+      win32u: Move window info private len to window shared memory.
+      server: Use a dedicated request to set window builtin FNID.
+      win32u: Implement real class names using builtin class FNID.
+      user32: Add helpers to set static control state.
+      user32: Set builtin class FNID for button controls.
+      user32: Set builtin class FNID for combo controls.
+      user32: Set builtin class FNID for edit controls.
+      user32: Set builtin class FNID for listbox controls.
+
+Shaun Ren (2):
+      mf/tests: Add some further tests for the presentation clock's SetTimer().
+      mf/clock: Insert timers in chronological order in present_clock_timer_SetTimer().
+
+Stefan Dösinger (2):
+      shell32: Add an Info icon.
+      shell32: Use IDI_SHELL_INFO icon for SIID_INFO.
+
+Stian Low (1):
+      wined3d: Add GPU information for Intel UHD 620 (0x5917).
+
+Trent Waddington (1):
+      crypt32: CertContext_SetProperty(CERT_NCRYPT_KEY_HANDLE_PROP_ID) takes a handle, not a pointer.
+
+Twaik Yont (7):
+      wineandroid: Restrict Java keyboard focus to whole Wine views.
+      wineandroid: Focus the desktop view after creating it.
+      wineandroid: Ignore duplicate primary button motion events.
+      wineandroid: Create desktop view via ioctl and pass event pipe.
+      wineandroid: Make JNI initialization work with direct JVM loading.
+      wineandroid: Terminate activity when desktop client disconnects.
+      wineandroid: Split Android driver and Wine into separate processes.
+
+Vibhav Pant (11):
+      winebth.sys: Fix potential NULL deref in bluez_gatt_characteristic_value_free.
+      winebth.sys: Push newly enumerated bluetooth devices directly to the watcher event queue.
+      winebth.sys: Factor code for handling new BlueZ objects into bluez_handle_new_object.
+      winebth.sys: Perform initial device enumeration inside a DBusPendingCall callback.
+      winebth.sys: Return the correct boolean value in bluez_gatt_characteristic_props_from_dict_entry.
+      windows.devices.bluetooth/tests: Add tests for IBluetoothLEDeviceStatics::FromBluetoothAddressAsync, IBluetoothLEDevice.
+      windows.devices.bluetooth: Implement BluetoothLEDeviceStatics::FromBluetoothAddressAsync.
+      windows.devices.bluetooth: Implement IBluetoothLEDevice::get_DeviceId.
+      windows.devices.bluetooth: Implement IBluetoothLEDevice::get_GattServices.
+      windows.devices.bluetooth: Implement IGattDeviceService::get_Uuid.
+      windows.devices.bluetooth: Implement IGattDeviceService::get_AttributeHandle.
+
+Zhiyi Zhang (9):
+      winex11.drv: Return WithdrawnState when get_window_wm_state() fails.
+      winex11.drv: Wait for the previous request when transitioning from IconicState to NormalState.
+      ntdll: Add NtAlpcCreatePort() stub.
+      ntdll: Add NtAlpcConnectPort() stub.
+      ntdll: Add NtAlpcAcceptConnectPort() stub.
+      ntdll: Add NtAlpcSendWaitReceivePort() stub.
+      ntdll: Add NtAlpcDisconnectPort() stub.
+      ntdll: Add NtAlpcImpersonateClientOfPort() stub.
+      ntdll: Add missing ZwAlpc* function names.
+
+Ziqing Hui (1):
+      dwrite: Add fallback for Miscellaneous Symbols.
 ```
diff --git a/AUTHORS b/AUTHORS
index 4ab36d544e2b..9efc8904d085 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,6 +6,7 @@ Aaron Barnes
 Aaron Brazener
 Aaron Hill
 Aaron Hope
+Aaron Yourk
 Aaryaman Vasishta
 Abey George
 Abraham Sudhakar
@@ -179,6 +180,7 @@ Anton Romanov
 Apostolos Alexiadis
 Aric Cyr
 Aric Stewart
+Arie Miller
 Arjen Nienhuis
 Arjun Comar
 Arkadiusz Hiler
@@ -207,6 +209,7 @@ Benedikt Bär
 Ben Hodgetts
 Benjamin Arai
 Benjamin Cutler
+Benjamin Fischer
 Benjamin Hodgetts
 Benjamin Kramer
 Benjamin Mayes
@@ -244,6 +247,7 @@ Bob Amstadt
 Bobby Bingham
 Bob Goodwin
 Bob Ziuchkovski
+Bohdan Tkachenko
 Borut Razem
 Boyuan Yang
 Bradan Fleming
@@ -571,6 +575,7 @@ Fan WenJie
 Farshad Agah
 Fatih Aşıcı
 Federico Schwindt
+Feifan He
 Felix Hädicke
 Felix Janda
 Felix Münchhalfen
@@ -911,6 +916,7 @@ Jody Goldberg
 Joel Holdsworth
 Joel Parker
 Joe Meyer
+Joerg Rueppel
 Joerg Schiermeier
 Joe Souza
 Johan Dahlin
@@ -1667,6 +1673,7 @@ Ron Cemer
 Ron Gage
 Ron Jensen
 Ron Record
+Rose Hellsing
 Ross Biro
 Royal Chan
 Royce Mitchell III
diff --git a/VERSION b/VERSION
index 756b98c957c4..3d72741195b6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Wine version 11.9
+Wine version 11.10
diff --git a/configure b/configure
index fda5e60e4152..6832c29abe58 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for Wine 11.9.
+# Generated by GNU Autoconf 2.72 for Wine 11.10.
 #
 # Report bugs to <wine-devel@winehq.org>.
 #
@@ -603,8 +603,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='Wine'
 PACKAGE_TARNAME='wine'
-PACKAGE_VERSION='11.9'
-PACKAGE_STRING='Wine 11.9'
+PACKAGE_VERSION='11.10'
+PACKAGE_STRING='Wine 11.10'
 PACKAGE_BUGREPORT='wine-devel@winehq.org'
 PACKAGE_URL='https://www.winehq.org'
 
@@ -1368,6 +1368,7 @@ enable_msxml6
 enable_mtxdm
 enable_ncrypt
 enable_nddeapi
+enable_ndfapi
 enable_ndis_sys
 enable_netapi32
 enable_netcfgx
@@ -1486,6 +1487,7 @@ enable_tbs
 enable_tdh
 enable_tdi_sys
 enable_threadpoolwinrt
+enable_thumbcache
 enable_traffic
 enable_twain_32
 enable_twaindsm
@@ -1600,6 +1602,7 @@ enable_wintab32
 enable_wintrust
 enable_wintypes
 enable_winusb
+enable_wkscli
 enable_wlanapi
 enable_wlanui
 enable_wldap32
@@ -2504,7 +2507,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-'configure' configures Wine 11.9 to adapt to many kinds of systems.
+'configure' configures Wine 11.10 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -2574,7 +2577,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Wine 11.9:";;
+     short | recursive ) echo "Configuration of Wine 11.10:";;
    esac
   cat <<\_ACEOF
 
@@ -2582,7 +2585,7 @@ Optional Features:
   --disable-option-checking  ignore unrecognized --enable/--with options
   --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
   --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
-  --enable-archs={i386,x86_64,arm,aarch64}
+  --enable-archs={i386,x86_64,arm,aarch64,arm64ec,none}
                           enable multiple architectures for PE compilation
   --disable-win16         do not include Win16 support
   --enable-win64          build a Win64 emulator on AMD64 (won't run Win32
@@ -2921,7 +2924,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Wine configure 11.9
+Wine configure 11.10
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -3327,7 +3330,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by Wine $as_me 11.9, which was
+It was created by Wine $as_me 11.10, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -23512,6 +23515,7 @@ wine_fn_config_makefile dlls/mtxdm enable_mtxdm
 wine_fn_config_makefile dlls/ncrypt enable_ncrypt
 wine_fn_config_makefile dlls/ncrypt/tests enable_tests
 wine_fn_config_makefile dlls/nddeapi enable_nddeapi
+wine_fn_config_makefile dlls/ndfapi enable_ndfapi
 wine_fn_config_makefile dlls/ndis.sys enable_ndis_sys
 wine_fn_config_makefile dlls/ndis.sys/tests enable_tests
 wine_fn_config_makefile dlls/netapi32 enable_netapi32
@@ -23706,6 +23710,8 @@ wine_fn_config_makefile dlls/tdh/tests enable_tests
 wine_fn_config_makefile dlls/tdi.sys enable_tdi_sys
 wine_fn_config_makefile dlls/threadpoolwinrt enable_threadpoolwinrt
 wine_fn_config_makefile dlls/threadpoolwinrt/tests enable_tests
+wine_fn_config_makefile dlls/thumbcache enable_thumbcache
+wine_fn_config_makefile dlls/thumbcache/tests enable_tests
 wine_fn_config_makefile dlls/toolhelp.dll16 enable_win16
 wine_fn_config_makefile dlls/traffic enable_traffic
 wine_fn_config_makefile dlls/twain.dll16 enable_win16
@@ -23905,6 +23911,7 @@ wine_fn_config_makefile dlls/wintrust/tests enable_tests
 wine_fn_config_makefile dlls/wintypes enable_wintypes
 wine_fn_config_makefile dlls/wintypes/tests enable_tests
 wine_fn_config_makefile dlls/winusb enable_winusb
+wine_fn_config_makefile dlls/wkscli enable_wkscli
 wine_fn_config_makefile dlls/wlanapi enable_wlanapi
 wine_fn_config_makefile dlls/wlanapi/tests enable_tests
 wine_fn_config_makefile dlls/wlanui enable_wlanui
@@ -24778,7 +24785,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by Wine $as_me 11.9, which was
+This file was extended by Wine $as_me 11.10, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -24838,7 +24845,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-Wine config.status 11.9
+Wine config.status 11.10
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
index 485a1370b068..60262f26f68c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,7 +16,7 @@ AC_PREREQ([2.69d],[AC_CONFIG_AUX_DIR(tools)],
 
 dnl **** Command-line arguments ****
 
-AC_ARG_ENABLE(archs, AS_HELP_STRING([--enable-archs={i386,x86_64,arm,aarch64}],[enable multiple architectures for PE compilation]))
+AC_ARG_ENABLE(archs, AS_HELP_STRING([--enable-archs={i386,x86_64,arm,aarch64,arm64ec,none}],[enable multiple architectures for PE compilation]))
 AC_ARG_ENABLE(win16, AS_HELP_STRING([--disable-win16],[do not include Win16 support]))
 AC_ARG_ENABLE(win64, AS_HELP_STRING([--enable-win64],[build a Win64 emulator on AMD64 (won't run Win32 binaries)]))
 AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[do not build the regression tests]))
@@ -3022,6 +3022,7 @@ WINE_CONFIG_MAKEFILE(dlls/mtxdm)
 WINE_CONFIG_MAKEFILE(dlls/ncrypt)
 WINE_CONFIG_MAKEFILE(dlls/ncrypt/tests)
 WINE_CONFIG_MAKEFILE(dlls/nddeapi)
+WINE_CONFIG_MAKEFILE(dlls/ndfapi)
 WINE_CONFIG_MAKEFILE(dlls/ndis.sys)
 WINE_CONFIG_MAKEFILE(dlls/ndis.sys/tests)
 WINE_CONFIG_MAKEFILE(dlls/netapi32)
@@ -3216,6 +3217,8 @@ WINE_CONFIG_MAKEFILE(dlls/tdh/tests)
 WINE_CONFIG_MAKEFILE(dlls/tdi.sys)
 WINE_CONFIG_MAKEFILE(dlls/threadpoolwinrt)
 WINE_CONFIG_MAKEFILE(dlls/threadpoolwinrt/tests)
+WINE_CONFIG_MAKEFILE(dlls/thumbcache)
+WINE_CONFIG_MAKEFILE(dlls/thumbcache/tests)
 WINE_CONFIG_MAKEFILE(dlls/toolhelp.dll16)
 WINE_CONFIG_MAKEFILE(dlls/traffic)
 WINE_CONFIG_MAKEFILE(dlls/twain.dll16)
@@ -3415,6 +3418,7 @@ WINE_CONFIG_MAKEFILE(dlls/wintrust/tests)
 WINE_CONFIG_MAKEFILE(dlls/wintypes)
 WINE_CONFIG_MAKEFILE(dlls/wintypes/tests)
 WINE_CONFIG_MAKEFILE(dlls/winusb)
+WINE_CONFIG_MAKEFILE(dlls/wkscli)
 WINE_CONFIG_MAKEFILE(dlls/wlanapi)
 WINE_CONFIG_MAKEFILE(dlls/wlanapi/tests)
 WINE_CONFIG_MAKEFILE(dlls/wlanui)
diff --git a/dlls/advapi32/advapi32.spec b/dlls/advapi32/advapi32.spec
index 273f382c5e6a..2496f4425aec 100644
--- a/dlls/advapi32/advapi32.spec
+++ b/dlls/advapi32/advapi32.spec
@@ -837,7 +837,7 @@
 # @ stub UsePinForEncryptedFilesW
 # @ stub WaitServiceState
 @ stub WdmWmiServiceMain
-@ stub WmiCloseBlock
+@ stdcall WmiCloseBlock(long)
 # @ stub WmiCloseTraceWithCursor
 # @ stub WmiConvertTimestamp
 # @ stub WmiDevInstToInstanceNameA
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c
index b565526725f0..9f76ec54209c 100644
--- a/dlls/advapi32/tests/security.c
+++ b/dlls/advapi32/tests/security.c
@@ -4271,6 +4271,16 @@ static void test_ConvertStringSecurityDescriptor(void)
        "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %lu\n", GetLastError());
     if (ret) LocalFree(pSD);
 
+    SetLastError(0xdeadbeef);
+    ret = ConvertStringSecurityDescriptorToSecurityDescriptorA(
+        "D: (D;OICI;GA;;;BG) (D;OICI;GA;;;AN) (A;OICI;GAGRGWGX;;;AU) (A;OICI;GA;;;BA)", SDDL_REVISION_1, &pSD, NULL);
+    ok(ret || broken(!ret && GetLastError() == ERROR_INVALID_DATATYPE) /* win2k */,
+       "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %lu\n", GetLastError());
+    acl = (ACL *)((char *)pSD + sizeof(SECURITY_DESCRIPTOR_RELATIVE));
+    ok(acl->AclSize == sizeof(*acl) * 12 /* 96 */, "got %u\n", acl->AclSize);
+    ok(acl->AceCount = 4, "got %u\n", acl->AceCount);
+    if (ret) LocalFree(pSD);
+
     /* empty DACL */
     size = 0;
     SetLastError(0xdeadbeef);
diff --git a/dlls/advapi32/wmi.c b/dlls/advapi32/wmi.c
index d35bcbe8d65f..3dcd9cfd0c4b 100644
--- a/dlls/advapi32/wmi.c
+++ b/dlls/advapi32/wmi.c
@@ -178,3 +178,12 @@ ULONG WMIAPI WmiSetSingleItemW(WMIHANDLE handle, const WCHAR *name, ULONG id, UL
     FIXME("%p %s %lu %lu %lu %p: stub\n", handle, debugstr_w(name), id, reserved, size, buffer);
     return ERROR_CALL_NOT_IMPLEMENTED;
 }
+
+/******************************************************************************
+ * WmiCloseBlock [ADVAPI32.@]
+ */
+ULONG WINAPI WmiCloseBlock(WMIHANDLE handle)
+{
+    FIXME("%p: stub\n", handle);
+    return ERROR_SUCCESS;
+}
diff --git a/dlls/amstream/audiostream.c b/dlls/amstream/audiostream.c
index 8e1c96b4b5e8..29f52d1b6c15 100644
--- a/dlls/amstream/audiostream.c
+++ b/dlls/amstream/audiostream.c
@@ -1210,8 +1210,7 @@ static HRESULT WINAPI audio_sink_NewSegment(IPin *iface, REFERENCE_TIME start, R
 {
     struct audio_stream *stream = impl_from_IPin(iface);
 
-    TRACE("stream %p, start %s, stop %s, rate %0.16e\n",
-            stream, wine_dbgstr_longlong(start), wine_dbgstr_longlong(stop), rate);
+    TRACE("stream %p, start %I64d, stop %I64d, rate %0.16e.\n", stream, start, stop, rate);
 
     EnterCriticalSection(&stream->cs);
 
diff --git a/dlls/amstream/ddrawstream.c b/dlls/amstream/ddrawstream.c
index 55ab7ab25d40..fc4eba59470f 100644
--- a/dlls/amstream/ddrawstream.c
+++ b/dlls/amstream/ddrawstream.c
@@ -1456,8 +1456,7 @@ static HRESULT WINAPI ddraw_sink_NewSegment(IPin *iface, REFERENCE_TIME start, R
 {
     struct ddraw_stream *stream = impl_from_IPin(iface);
 
-    TRACE("stream %p, start %s, stop %s, rate %0.16e\n",
-            stream, wine_dbgstr_longlong(start), wine_dbgstr_longlong(stop), rate);
+    TRACE("stream %p, start %I64d, stop %I64d, rate %0.16e.\n", stream, start, stop, rate);
 
     EnterCriticalSection(&stream->cs);
 
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c
index 8a7e4fc372cc..ae03cdee76d8 100644
--- a/dlls/amstream/filter.c
+++ b/dlls/amstream/filter.c
@@ -345,7 +345,7 @@ static HRESULT WINAPI filter_Run(IMediaStreamFilter *iface, REFERENCE_TIME start
 {
     struct filter *filter = impl_from_IMediaStreamFilter(iface);
 
-    TRACE("iface %p, start %s.\n", iface, wine_dbgstr_longlong(start));
+    TRACE("iface %p, start %I64d.\n", iface, start);
 
     EnterCriticalSection(&filter->cs);
 
@@ -728,7 +728,7 @@ static HRESULT WINAPI filter_WaitUntil(IMediaStreamFilter *iface, REFERENCE_TIME
     struct list *entry;
     HRESULT hr;
 
-    TRACE("filter %p, time %s.\n", iface, wine_dbgstr_longlong(time));
+    TRACE("filter %p, time %I64d.\n", iface, time);
 
     EnterCriticalSection(&filter->cs);
 
@@ -993,8 +993,8 @@ static HRESULT WINAPI filter_seeking_GetCurrentPosition(IMediaSeeking *iface, LO
 static HRESULT WINAPI filter_seeking_ConvertTimeFormat(IMediaSeeking *iface, LONGLONG *target,
         const GUID *target_format, LONGLONG source, const GUID *source_format)
 {
-    FIXME("iface %p, target %p, target_format %s, source 0x%s, source_format %s, stub!\n", iface, target, debugstr_guid(target_format),
-            wine_dbgstr_longlong(source), debugstr_guid(source_format));
+    FIXME("iface %p, target %p, target_format %s, source %#I64x, source_format %s, stub!\n",
+            iface, target, debugstr_guid(target_format), source, debugstr_guid(source_format));
 
     return E_NOTIMPL;
 }
@@ -1006,9 +1006,8 @@ static HRESULT WINAPI filter_seeking_SetPositions(IMediaSeeking *iface, LONGLONG
     IMediaSeeking *seeking;
     HRESULT hr;
 
-    TRACE("iface %p, current %s, current_flags %#lx, stop %s, stop_flags %#lx.\n", iface,
-            current_ptr ? wine_dbgstr_longlong(*current_ptr) : "<null>", current_flags,
-            stop_ptr ? wine_dbgstr_longlong(*stop_ptr): "<null>", stop_flags);
+    TRACE("iface %p, current %p, current_flags %#lx, stop %p, stop_flags %#lx.\n",
+            iface, current_ptr, current_flags, stop_ptr, stop_flags);
 
     EnterCriticalSection(&filter->cs);
 
diff --git a/dlls/amstream/multimedia.c b/dlls/amstream/multimedia.c
index dc855901c77b..29203f0697a9 100644
--- a/dlls/amstream/multimedia.c
+++ b/dlls/amstream/multimedia.c
@@ -200,11 +200,11 @@ static HRESULT WINAPI multimedia_stream_GetDuration(IAMMultiMediaStream *iface,
 
 static HRESULT WINAPI multimedia_stream_Seek(IAMMultiMediaStream *iface, STREAM_TIME seek_time)
 {
-    struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
+    struct multimedia_stream *mmstream = impl_from_IAMMultiMediaStream(iface);
 
-    TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(seek_time));
+    TRACE("mmstream %p, seek_time %I64d.\n", mmstream, seek_time);
 
-    return IMediaSeeking_SetPositions(This->media_seeking, &seek_time, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
+    return IMediaSeeking_SetPositions(mmstream->media_seeking, &seek_time, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
 }
 
 static HRESULT WINAPI multimedia_stream_GetEndOfStream(IAMMultiMediaStream *iface, HANDLE *eos)
diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c
index b3f555fa735d..dbff4eb42f9b 100644
--- a/dlls/amstream/tests/amstream.c
+++ b/dlls/amstream/tests/amstream.c
@@ -621,7 +621,7 @@ static void test_mmstream_get_duration(const WCHAR *test_avi_path)
     duration = 0xdeadbeefdeadbeefULL;
     hr = IAMMultiMediaStream_GetDuration(mmstream, &duration);
     ok(hr == E_NOINTERFACE, "Got hr %#lx.\n", hr);
-    ok(duration == 0xdeadbeefdeadbeefULL, "Got duration %s.\n", wine_dbgstr_longlong(duration));
+    ok(duration == 0xdeadbeefdeadbeefULL, "Got duration %I64d.\n", duration);
 
     hr = IAMMultiMediaStream_AddMediaStream(mmstream, NULL, &MSPID_PrimaryVideo, 0, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -638,12 +638,12 @@ static void test_mmstream_get_duration(const WCHAR *test_avi_path)
     if (audio_hr == S_OK)
     {
         ok(hr == S_OK, "Got hr %#lx.\n", hr);
-        ok(duration == 1000000LL, "Got duration %s.\n", wine_dbgstr_longlong(duration));
+        ok(duration == 1000000LL, "Got duration %I64d.\n", duration);
     }
     else
     {
         ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
-        ok(!duration, "Got duration %s.\n", wine_dbgstr_longlong(duration));
+        ok(!duration, "Got duration %I64d.\n", duration);
     }
 
     ref = IAMMultiMediaStream_Release(mmstream);
@@ -656,7 +656,7 @@ static void test_mmstream_get_duration(const WCHAR *test_avi_path)
     duration = 0xdeadbeefdeadbeefULL;
     hr = IAMMultiMediaStream_GetDuration(mmstream, &duration);
     ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
-    ok(duration == 0, "Got duration %s.\n", wine_dbgstr_longlong(duration));
+    ok(duration == 0, "Got duration %I64d.\n", duration);
 
     ref = IAMMultiMediaStream_Release(mmstream);
     ok(!ref, "Got outstanding refcount %ld.\n", ref);
@@ -669,7 +669,7 @@ static void test_mmstream_get_duration(const WCHAR *test_avi_path)
     duration = 0xdeadbeefdeadbeefULL;
     hr = IAMMultiMediaStream_GetDuration(mmstream, &duration);
     ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
-    ok(duration == 0, "Got duration %s.\n", wine_dbgstr_longlong(duration));
+    ok(duration == 0, "Got duration %I64d.\n", duration);
 
     ref = IAMMultiMediaStream_Release(mmstream);
     ok(!ref, "Got outstanding refcount %ld.\n", ref);
@@ -684,7 +684,7 @@ static void test_mmstream_get_duration(const WCHAR *test_avi_path)
     duration = 0xdeadbeefdeadbeefULL;
     hr = IAMMultiMediaStream_GetDuration(mmstream, &duration);
     ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
-    ok(duration == 0, "Got duration %s.\n", wine_dbgstr_longlong(duration));
+    ok(duration == 0, "Got duration %I64d.\n", duration);
 
     ref = IAMMultiMediaStream_Release(mmstream);
     ok(!ref, "Got outstanding refcount %ld.\n", ref);
@@ -4858,8 +4858,8 @@ static void test_audiostream_new_segment(void)
     end_time = 0xdeadbeefdeadbeef;
     hr = IAudioStreamSample_GetSampleTimes(stream_sample, &start_time, &end_time, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start_time == 23456789, "Got start time %s.\n", wine_dbgstr_longlong(start_time));
-    ok(end_time == 23459057, "Got end time %s.\n", wine_dbgstr_longlong(end_time));
+    ok(start_time == 23456789, "Got start time %I64d.\n", start_time);
+    ok(end_time == 23459057, "Got end time %I64d.\n", end_time);
 
     hr = IPin_NewSegment(pin, 11111111, 22222222, 2.0);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -4880,8 +4880,8 @@ static void test_audiostream_new_segment(void)
     end_time = 0xdeadbeefdeadbeef;
     hr = IAudioStreamSample_GetSampleTimes(stream_sample, &start_time, &end_time, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start_time == 23456789, "Got start time %s.\n", wine_dbgstr_longlong(start_time));
-    ok(end_time == 23459057, "Got end time %s.\n", wine_dbgstr_longlong(end_time));
+    ok(start_time == 23456789, "Got start time %I64d.\n", start_time);
+    ok(end_time == 23459057, "Got end time %I64d.\n", end_time);
 
     hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -5424,7 +5424,7 @@ static void test_audiostreamsample_get_sample_times(void)
     current_time = 0xdeadbeefdeadbeef;
     hr = IAudioStreamSample_GetSampleTimes(stream_sample, NULL, NULL, &current_time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current_time == 0, "Got current time %s.\n", wine_dbgstr_longlong(current_time));
+    ok(current_time == 0, "Got current time %I64d.\n", current_time);
 
     hr = IMediaFilter_SetSyncSource(graph_media_filter, &clock.IReferenceClock_iface);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -5432,7 +5432,7 @@ static void test_audiostreamsample_get_sample_times(void)
     current_time = 0xdeadbeefdeadbeef;
     hr = IAudioStreamSample_GetSampleTimes(stream_sample, NULL, NULL, &current_time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current_time == 0, "Got current time %s.\n", wine_dbgstr_longlong(current_time));
+    ok(current_time == 0, "Got current time %I64d.\n", current_time);
 
     hr = IGraphBuilder_ConnectDirect(graph, &source.source.pin.IPin_iface, pin, &audio_mt);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -5447,31 +5447,31 @@ static void test_audiostreamsample_get_sample_times(void)
     current_time = 0xdeadbeefdeadbeef;
     hr = IAudioStreamSample_GetSampleTimes(stream_sample, NULL, NULL, &current_time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current_time == 0xdeadbeefddf15da1 + filter_start_time, "Expected current time %s, got %s.\n",
-            wine_dbgstr_longlong(0xdeadbeefddf15da1 + filter_start_time), wine_dbgstr_longlong(current_time));
+    ok(current_time == 0xdeadbeefddf15da1 + filter_start_time, "Expected current time %I64d, got %I64d.\n",
+            0xdeadbeefddf15da1 + filter_start_time, current_time);
 
     clock.get_time_hr = S_OK;
 
     current_time = 0xdeadbeefdeadbeef;
     hr = IAudioStreamSample_GetSampleTimes(stream_sample, NULL, NULL, &current_time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current_time == filter_start_time, "Expected current time %s, got %s.\n",
-            wine_dbgstr_longlong(filter_start_time), wine_dbgstr_longlong(current_time));
+    ok(current_time == filter_start_time, "Expected current time %I64d, got %I64d.\n",
+            filter_start_time, current_time);
 
     clock.time = 23456789;
 
     current_time = 0xdeadbeefdeadbeef;
     hr = IAudioStreamSample_GetSampleTimes(stream_sample, NULL, NULL, &current_time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current_time == filter_start_time + 11111111, "Expected current time %s, got %s.\n",
-            wine_dbgstr_longlong(filter_start_time + 11111111), wine_dbgstr_longlong(current_time));
+    ok(current_time == filter_start_time + 11111111, "Expected current time %I64d, got %I64d.\n",
+            filter_start_time + 11111111, current_time);
 
     start_time = 0xdeadbeefdeadbeef;
     end_time = 0xdeadbeefdeadbeef;
     hr = IAudioStreamSample_GetSampleTimes(stream_sample, &start_time, &end_time, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start_time == 0, "Got start time %s.\n", wine_dbgstr_longlong(start_time));
-    ok(end_time == 0, "Got end time %s.\n", wine_dbgstr_longlong(end_time));
+    ok(start_time == 0, "Got start time %I64d.\n", start_time);
+    ok(end_time == 0, "Got end time %I64d.\n", end_time);
 
     media_sample = ammediastream_allocate_sample(&source, test_data, 8);
     start_time = 12345678;
@@ -5489,8 +5489,8 @@ static void test_audiostreamsample_get_sample_times(void)
     end_time = 0xdeadbeefdeadbeef;
     hr = IAudioStreamSample_GetSampleTimes(stream_sample, &start_time, &end_time, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start_time == 12345678, "Got start time %s.\n", wine_dbgstr_longlong(start_time));
-    ok(end_time == 12347946, "Got end time %s.\n", wine_dbgstr_longlong(end_time));
+    ok(start_time == 12345678, "Got start time %I64d.\n", start_time);
+    ok(end_time == 12347946, "Got end time %I64d.\n", end_time);
 
     media_sample = ammediastream_allocate_sample(&source, test_data, 6);
     start_time = 12345678;
@@ -5508,8 +5508,8 @@ static void test_audiostreamsample_get_sample_times(void)
     end_time = 0xdeadbeefdeadbeef;
     hr = IAudioStreamSample_GetSampleTimes(stream_sample, &start_time, &end_time, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start_time == 12347946, "Got start time %s.\n", wine_dbgstr_longlong(start_time));
-    ok(end_time == 12346585, "Got end time %s.\n", wine_dbgstr_longlong(end_time));
+    ok(start_time == 12347946, "Got start time %I64d.\n", start_time);
+    ok(end_time == 12346585, "Got end time %I64d.\n", end_time);
 
     hr = IPin_EndOfStream(pin);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -5521,8 +5521,8 @@ static void test_audiostreamsample_get_sample_times(void)
     end_time = 0xdeadbeefdeadbeef;
     hr = IAudioStreamSample_GetSampleTimes(stream_sample, &start_time, &end_time, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start_time == 12346585, "Got start time %s.\n", wine_dbgstr_longlong(start_time));
-    ok(end_time == 12348399, "Got end time %s.\n", wine_dbgstr_longlong(end_time));
+    ok(start_time == 12346585, "Got start time %I64d.\n", start_time);
+    ok(end_time == 12348399, "Got end time %I64d.\n", end_time);
 
     hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -6536,8 +6536,8 @@ static void test_ddrawstream_new_segment(void)
     end_time = 0xdeadbeefdeadbeef;
     hr = IDirectDrawStreamSample_GetSampleTimes(stream_sample, &start_time, &end_time, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start_time == 23456789, "Got start time %s.\n", wine_dbgstr_longlong(start_time));
-    ok(end_time == 34567900, "Got end time %s.\n", wine_dbgstr_longlong(end_time));
+    ok(start_time == 23456789, "Got start time %I64d.\n", start_time);
+    ok(end_time == 34567900, "Got end time %I64d.\n", end_time);
 
     hr = IPin_NewSegment(pin, 11111111, 22222222, 2.0);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -6558,8 +6558,8 @@ static void test_ddrawstream_new_segment(void)
     end_time = 0xdeadbeefdeadbeef;
     hr = IDirectDrawStreamSample_GetSampleTimes(stream_sample, &start_time, &end_time, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start_time == 23456789, "Got start time %s.\n", wine_dbgstr_longlong(start_time));
-    ok(end_time == 34567900, "Got end time %s.\n", wine_dbgstr_longlong(end_time));
+    ok(start_time == 23456789, "Got start time %I64d.\n", start_time);
+    ok(end_time == 34567900, "Got end time %I64d.\n", end_time);
 
     hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -6625,7 +6625,7 @@ static void test_ddrawstream_get_time_per_frame(void)
     frame_time = 0xdeadbeefdeadbeef;
     hr = IDirectDrawMediaStream_GetTimePerFrame(ddraw_stream, &frame_time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(frame_time == 12345678, "Got frame time %s.\n", wine_dbgstr_longlong(frame_time));
+    ok(frame_time == 12345678, "Got frame time %I64d.\n", frame_time);
 
     ref = IAMMultiMediaStream_Release(mmstream);
     ok(!ref, "Got outstanding refcount %ld.\n", ref);
@@ -7341,21 +7341,21 @@ static void check_mediastreamfilter_set_positions(IMediaSeeking *seeking, struct
     hr = IMediaSeeking_SetPositions(seeking, &current_position, AM_SEEKING_AbsolutePositioning,
             &stop_position, AM_SEEKING_AbsolutePositioning);
     ok(hr == expected_hr, "Got hr %#lx.\n", hr);
-    ok(source1->current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n",
-            wine_dbgstr_longlong(source1->current_position));
-    ok(source1->stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n",
-            wine_dbgstr_longlong(source1->stop_position));
+    ok(source1->current_position == 0xdeadbeefdeadbeefULL, "Got current position %I64d.\n",
+            source1->current_position);
+    ok(source1->stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %I64d.\n",
+            source1->stop_position);
     if (SUCCEEDED(expected_hr))
     {
-        ok(source2->current_position == 12345678, "Got current position %s.\n",
-                wine_dbgstr_longlong(source2->current_position));
-        ok(source2->stop_position == 87654321, "Got stop position %s.\n",
-                wine_dbgstr_longlong(source2->stop_position));
+        ok(source2->current_position == 12345678, "Got current position %I64d.\n",
+                source2->current_position);
+        ok(source2->stop_position == 87654321, "Got stop position %I64d.\n",
+                source2->stop_position);
     }
-    ok(source3->current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n",
-            wine_dbgstr_longlong(source3->current_position));
-    ok(source3->stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n",
-            wine_dbgstr_longlong(source3->stop_position));
+    ok(source3->current_position == 0xdeadbeefdeadbeefULL, "Got current position %I64d.\n",
+            source3->current_position);
+    ok(source3->stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %I64d.\n",
+            source3->stop_position);
 }
 
 static void check_mediastreamfilter_get_duration(IMediaSeeking *seeking, struct testfilter *source1,
@@ -7368,9 +7368,9 @@ static void check_mediastreamfilter_get_duration(IMediaSeeking *seeking, struct
     hr = IMediaSeeking_GetDuration(seeking, &duration);
     ok(hr == expected_hr, "Got hr %#lx.\n", hr);
     if (SUCCEEDED(expected_hr))
-        ok(duration == 0x8000000000000000ULL, "Got duration %s.\n", wine_dbgstr_longlong(duration));
+        ok(duration == 0x8000000000000000ULL, "Got duration %I64d.\n", duration);
     else
-        ok(duration == 0xdeadbeefdeadbeefULL, "Got duration %s.\n", wine_dbgstr_longlong(duration));
+        ok(duration == 0xdeadbeefdeadbeefULL, "Got duration %I64d.\n", duration);
 }
 
 static void check_mediastreamfilter_get_stop_position(IMediaSeeking *seeking, struct testfilter *source1,
@@ -7383,9 +7383,9 @@ static void check_mediastreamfilter_get_stop_position(IMediaSeeking *seeking, st
     hr = IMediaSeeking_GetStopPosition(seeking, &stop);
     ok(hr == expected_hr, "Got hr %#lx.\n", hr);
     if (SUCCEEDED(expected_hr))
-        ok(stop == 0x8000000000000000ULL, "Got stop position %s.\n", wine_dbgstr_longlong(stop));
+        ok(stop == 0x8000000000000000ULL, "Got stop position %I64d.\n", stop);
     else
-        ok(stop == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n", wine_dbgstr_longlong(stop));
+        ok(stop == 0xdeadbeefdeadbeefULL, "Got stop position %I64d.\n", stop);
 }
 
 static void check_mediastreamfilter_is_format_supported(IMediaSeeking *seeking, struct testfilter *source1,
@@ -7444,11 +7444,11 @@ static void test_mediastreamfilter_get_current_stream_time(void)
     time = 0xdeadbeefdeadbeef;
     hr = IMediaStreamFilter_GetCurrentStreamTime(filter, &time);
     ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
-    ok(time == 0, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0, "Got time %I64d.\n", time);
     time = 0xdeadbeefdeadbeef;
     hr = IAMMultiMediaStream_GetTime(mmstream, &time);
     ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
-    ok(time == 0, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0, "Got time %I64d.\n", time);
 
     hr = IMediaFilter_SetSyncSource(media_filter, &clock.IReferenceClock_iface);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -7458,11 +7458,11 @@ static void test_mediastreamfilter_get_current_stream_time(void)
     time = 0xdeadbeefdeadbeef;
     hr = IMediaStreamFilter_GetCurrentStreamTime(filter, &time);
     ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
-    ok(time == 0, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0, "Got time %I64d.\n", time);
     time = 0xdeadbeefdeadbeef;
     hr = IAMMultiMediaStream_GetTime(mmstream, &time);
     ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
-    ok(time == 0, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0, "Got time %I64d.\n", time);
 
     clock.time = 23456789;
     clock.get_time_hr = S_OK;
@@ -7478,11 +7478,11 @@ static void test_mediastreamfilter_get_current_stream_time(void)
     time = 0xdeadbeefdeadbeef;
     hr = IMediaStreamFilter_GetCurrentStreamTime(filter, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 11111101 + filter_start_time, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 11111101 + filter_start_time, "Got time %I64d.\n", time);
     time = 0xdeadbeefdeadbeef;
     hr = IAMMultiMediaStream_GetTime(mmstream, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 11111101 + filter_start_time, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 11111101 + filter_start_time, "Got time %I64d.\n", time);
 
     ref = IAMMultiMediaStream_Release(mmstream);
     ok(!ref, "Got outstanding refcount %ld.\n", ref);
@@ -7512,7 +7512,7 @@ static void test_mediastreamfilter_reference_time_to_stream_time(void)
     time = 0xdeadbeefdeadbeef;
     hr = IMediaStreamFilter_ReferenceTimeToStreamTime(filter, &time);
     ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
-    ok(time == 0xdeadbeefdeadbeef, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0xdeadbeefdeadbeef, "Got time %I64d.\n", time);
 
     hr = IMediaStreamFilter_SetSyncSource(filter, &clock.IReferenceClock_iface);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -7529,7 +7529,7 @@ static void test_mediastreamfilter_reference_time_to_stream_time(void)
     time = 0xdeadbeefdeadbeef;
     hr = IMediaStreamFilter_ReferenceTimeToStreamTime(filter, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0xdeadbeefdeadbeef, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0xdeadbeefdeadbeef, "Got time %I64d.\n", time);
 
     hr = IMediaStreamFilter_Run(filter, 23456789);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -7537,7 +7537,7 @@ static void test_mediastreamfilter_reference_time_to_stream_time(void)
     time = 0xdeadbeefdeadbeef;
     hr = IMediaStreamFilter_ReferenceTimeToStreamTime(filter, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0xdeadbeefdd47d2da, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0xdeadbeefdd47d2da, "Got time %I64d.\n", time);
 
     clock.time = 34567890;
     clock.get_time_hr = S_OK;
@@ -7545,7 +7545,7 @@ static void test_mediastreamfilter_reference_time_to_stream_time(void)
     time = 0xdeadbeefdeadbeef;
     hr = IMediaStreamFilter_ReferenceTimeToStreamTime(filter, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0xdeadbeefdd47d2da, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0xdeadbeefdd47d2da, "Got time %I64d.\n", time);
 
     ref = IMediaStreamFilter_Release(filter);
     ok(!ref, "Got outstanding refcount %ld.\n", ref);
@@ -7610,8 +7610,8 @@ static void test_mediastreamfilter_wait_until(void)
     ok(!WaitForSingleObject(cookie1.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
     ok(WaitForSingleObject(thread1, 100) == WAIT_TIMEOUT, "WaitUntil returned prematurely.\n");
 
-    ok(cookie1.base == 23456789, "Got base %s.\n", wine_dbgstr_longlong(cookie1.base));
-    ok(cookie1.offset == 12345678, "Got offset %s.\n", wine_dbgstr_longlong(cookie1.offset));
+    ok(cookie1.base == 23456789, "Got base %I64d.\n", cookie1.base);
+    ok(cookie1.offset == 12345678, "Got offset %I64d.\n", cookie1.offset);
     ok(!!cookie1.event, "Expected non-NULL event.\n");
 
     SetEvent(cookie1.event);
@@ -7635,8 +7635,8 @@ static void test_mediastreamfilter_wait_until(void)
     ok(!WaitForSingleObject(cookie1.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
     ok(WaitForSingleObject(thread1, 100) == WAIT_TIMEOUT, "WaitUntil returned prematurely.\n");
 
-    ok(cookie1.base == 23456789, "Got base %s.\n", wine_dbgstr_longlong(cookie1.base));
-    ok(cookie1.offset == 12345678, "Got offset %s.\n", wine_dbgstr_longlong(cookie1.offset));
+    ok(cookie1.base == 23456789, "Got base %I64d.\n", cookie1.base);
+    ok(cookie1.offset == 12345678, "Got offset %I64d.\n", cookie1.offset);
     ok(!!cookie1.event, "Expected non-NULL event.\n");
 
     clock.advise_time_cookie = &cookie2;
@@ -7648,8 +7648,8 @@ static void test_mediastreamfilter_wait_until(void)
     ok(!WaitForSingleObject(cookie2.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
     ok(WaitForSingleObject(thread2, 100) == WAIT_TIMEOUT, "WaitUntil returned prematurely.\n");
 
-    ok(cookie2.base == 11111111, "Got base %s.\n", wine_dbgstr_longlong(cookie2.base));
-    ok(cookie2.offset == 12345678, "Got offset %s.\n", wine_dbgstr_longlong(cookie2.offset));
+    ok(cookie2.base == 11111111, "Got base %I64d.\n", cookie2.base);
+    ok(cookie2.offset == 12345678, "Got offset %I64d.\n", cookie2.offset);
     ok(!!cookie2.event, "Expected non-NULL event.\n");
 
     SetEvent(cookie1.event);
@@ -8713,9 +8713,9 @@ static void test_ddrawstream_qc(void)
     ok(source.qc_notify_quality.Proportion == 0xdeadbeef, "Got proportion %ld.\n",
             source.qc_notify_quality.Proportion);
     ok(source.qc_notify_quality.Late == 0xdeadbeef,
-            "Got late %s.\n", wine_dbgstr_longlong(source.qc_notify_quality.Late));
-    ok(source.qc_notify_quality.TimeStamp == 0xdeadbeef, "Got time stamp %s.\n",
-            wine_dbgstr_longlong(source.qc_notify_quality.TimeStamp));
+            "Got late %I64d.\n", source.qc_notify_quality.Late);
+    ok(source.qc_notify_quality.TimeStamp == 0xdeadbeef, "Got time stamp %I64d.\n",
+            source.qc_notify_quality.TimeStamp);
 
     hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -8762,9 +8762,9 @@ static void test_ddrawstream_qc(void)
     ok(source.qc_notify_quality.Proportion == 0xdeadbeef, "Got proportion %ld.\n",
             source.qc_notify_quality.Proportion);
     ok(source.qc_notify_quality.Late == 0xdeadbeef,
-            "Got late %s.\n", wine_dbgstr_longlong(source.qc_notify_quality.Late));
-    ok(source.qc_notify_quality.TimeStamp == 0xdeadbeef, "Got time stamp %s.\n",
-            wine_dbgstr_longlong(source.qc_notify_quality.TimeStamp));
+            "Got late %I64d.\n", source.qc_notify_quality.Late);
+    ok(source.qc_notify_quality.TimeStamp == 0xdeadbeef, "Got time stamp %I64d.\n",
+            source.qc_notify_quality.TimeStamp);
 
     clock.time = 12345678 - filter_start_time + 11111111 + 200000;
 
@@ -8782,10 +8782,10 @@ static void test_ddrawstream_qc(void)
             source.qc_notify_quality.Type);
     ok(source.qc_notify_quality.Proportion == 1000, "Got proportion %ld.\n",
             source.qc_notify_quality.Proportion);
-    ok(source.qc_notify_quality.Late == 0, "Got late %s.\n",
-            wine_dbgstr_longlong(source.qc_notify_quality.Late));
-    ok(source.qc_notify_quality.TimeStamp == start_time, "Got time stamp %s.\n",
-            wine_dbgstr_longlong(source.qc_notify_quality.TimeStamp));
+    ok(source.qc_notify_quality.Late == 0, "Got late %I64d.\n",
+            source.qc_notify_quality.Late);
+    ok(source.qc_notify_quality.TimeStamp == start_time, "Got time stamp %I64d.\n",
+            source.qc_notify_quality.TimeStamp);
 
     /* Test Update() after Reveive(). */
     source.qc_notify_sender = (IBaseFilter *)0xdeadbeef;
@@ -8815,9 +8815,9 @@ static void test_ddrawstream_qc(void)
     ok(source.qc_notify_quality.Proportion == 0xdeadbeef, "Got proportion %ld.\n",
             source.qc_notify_quality.Proportion);
     ok(source.qc_notify_quality.Late == 0xdeadbeef,
-            "Got late %s.\n", wine_dbgstr_longlong(source.qc_notify_quality.Late));
-    ok(source.qc_notify_quality.TimeStamp == 0xdeadbeef, "Got time stamp %s.\n",
-            wine_dbgstr_longlong(source.qc_notify_quality.TimeStamp));
+            "Got late %I64d.\n", source.qc_notify_quality.Late);
+    ok(source.qc_notify_quality.TimeStamp == 0xdeadbeef, "Got time stamp %I64d.\n",
+            source.qc_notify_quality.TimeStamp);
 
     clock.time = 12345678 - filter_start_time + 11111111 + 500000;
 
@@ -8833,10 +8833,10 @@ static void test_ddrawstream_qc(void)
             source.qc_notify_quality.Type);
     ok(source.qc_notify_quality.Proportion == 1000, "Got proportion %ld.\n",
             source.qc_notify_quality.Proportion);
-    ok(source.qc_notify_quality.Late == 100000, "Got late %s.\n",
-            wine_dbgstr_longlong(source.qc_notify_quality.Late));
-    ok(source.qc_notify_quality.TimeStamp == start_time, "Got time stamp %s.\n",
-            wine_dbgstr_longlong(source.qc_notify_quality.TimeStamp));
+    ok(source.qc_notify_quality.Late == 100000, "Got late %I64d.\n",
+            source.qc_notify_quality.Late);
+    ok(source.qc_notify_quality.TimeStamp == start_time, "Got time stamp %I64d.\n",
+            source.qc_notify_quality.TimeStamp);
 
     hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -10671,8 +10671,8 @@ static void test_ddrawstreamsample_completion_status(void)
     ok(!WaitForSingleObject(cookie.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
     ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
 
-    ok(cookie.base == 11111111 + 10000, "Got base %s.\n", wine_dbgstr_longlong(cookie.base));
-    ok(cookie.offset == 12345678 - filter_start_time, "Got offset %s.\n", wine_dbgstr_longlong(cookie.offset));
+    ok(cookie.base == 11111111 + 10000, "Got base %I64d.\n", cookie.base);
+    ok(cookie.offset == 12345678 - filter_start_time, "Got offset %I64d.\n", cookie.offset);
 
     hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, 0, 0);
     ok(hr == MS_S_PENDING, "Got hr %#lx.\n", hr);
@@ -10698,8 +10698,8 @@ static void test_ddrawstreamsample_completion_status(void)
     ok(!WaitForSingleObject(cookie.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
     ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
 
-    ok(cookie.base == 11111111 + 20000, "Got base %s.\n", wine_dbgstr_longlong(cookie.base));
-    ok(cookie.offset == 12345678 - filter_start_time, "Got offset %s.\n", wine_dbgstr_longlong(cookie.offset));
+    ok(cookie.base == 11111111 + 20000, "Got base %I64d.\n", cookie.base);
+    ok(cookie.offset == 12345678 - filter_start_time, "Got offset %I64d.\n", cookie.offset);
 
     hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, COMPSTAT_NOUPDATEOK, 0);
     ok(hr == MS_S_NOUPDATE, "Got hr %#lx.\n", hr);
@@ -10727,8 +10727,8 @@ static void test_ddrawstreamsample_completion_status(void)
     ok(!WaitForSingleObject(cookie.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
     ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
 
-    ok(cookie.base == 11111111 + 30000, "Got base %s.\n", wine_dbgstr_longlong(cookie.base));
-    ok(cookie.offset == 12345678 - filter_start_time, "Got offset %s.\n", wine_dbgstr_longlong(cookie.offset));
+    ok(cookie.base == 11111111 + 30000, "Got base %I64d.\n", cookie.base);
+    ok(cookie.offset == 12345678 - filter_start_time, "Got offset %I64d.\n", cookie.offset);
 
     hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, COMPSTAT_NOUPDATEOK | COMPSTAT_WAIT, INFINITE);
     ok(hr == MS_S_NOUPDATE, "Got hr %#lx.\n", hr);
@@ -10756,8 +10756,8 @@ static void test_ddrawstreamsample_completion_status(void)
     ok(!WaitForSingleObject(cookie.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
     ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
 
-    ok(cookie.base == 11111111 + 40000, "Got base %s.\n", wine_dbgstr_longlong(cookie.base));
-    ok(cookie.offset == 12345678 - filter_start_time, "Got offset %s.\n", wine_dbgstr_longlong(cookie.offset));
+    ok(cookie.base == 11111111 + 40000, "Got base %I64d.\n", cookie.base);
+    ok(cookie.offset == 12345678 - filter_start_time, "Got offset %I64d.\n", cookie.offset);
 
     hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, COMPSTAT_ABORT, 0);
     ok(hr == MS_S_NOUPDATE, "Got hr %#lx.\n", hr);
@@ -10784,8 +10784,8 @@ static void test_ddrawstreamsample_completion_status(void)
     ok(!WaitForSingleObject(cookie.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
     ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
 
-    ok(cookie.base == 11111111 + 50000, "Got base %s.\n", wine_dbgstr_longlong(cookie.base));
-    ok(cookie.offset == 12345678 - filter_start_time, "Got offset %s.\n", wine_dbgstr_longlong(cookie.offset));
+    ok(cookie.base == 11111111 + 50000, "Got base %I64d.\n", cookie.base);
+    ok(cookie.offset == 12345678 - filter_start_time, "Got offset %I64d.\n", cookie.offset);
 
     hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -10880,7 +10880,7 @@ static void test_ddrawstreamsample_get_sample_times(void)
     current_time = 0xdeadbeefdeadbeef;
     hr = IDirectDrawStreamSample_GetSampleTimes(stream_sample, NULL, NULL, &current_time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current_time == 0, "Got current time %s.\n", wine_dbgstr_longlong(current_time));
+    ok(current_time == 0, "Got current time %I64d.\n", current_time);
 
     hr = IMediaFilter_SetSyncSource(graph_media_filter, &clock.IReferenceClock_iface);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -10888,7 +10888,7 @@ static void test_ddrawstreamsample_get_sample_times(void)
     current_time = 0xdeadbeefdeadbeef;
     hr = IDirectDrawStreamSample_GetSampleTimes(stream_sample, NULL, NULL, &current_time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current_time == 0, "Got current time %s.\n", wine_dbgstr_longlong(current_time));
+    ok(current_time == 0, "Got current time %I64d.\n", current_time);
 
     hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -10901,31 +10901,31 @@ static void test_ddrawstreamsample_get_sample_times(void)
     current_time = 0xdeadbeefdeadbeef;
     hr = IDirectDrawStreamSample_GetSampleTimes(stream_sample, NULL, NULL, &current_time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current_time == 0xdeadbeefddf15da1 + filter_start_time, "Expected current time %s, got %s.\n",
-            wine_dbgstr_longlong(0xdeadbeefddf15da1 + filter_start_time), wine_dbgstr_longlong(current_time));
+    ok(current_time == 0xdeadbeefddf15da1 + filter_start_time, "Expected current time %I64d, got %I64d.\n",
+            0xdeadbeefddf15da1 + filter_start_time, current_time);
 
     clock.get_time_hr = S_OK;
 
     current_time = 0xdeadbeefdeadbeef;
     hr = IDirectDrawStreamSample_GetSampleTimes(stream_sample, NULL, NULL, &current_time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current_time == filter_start_time, "Expected current time %s, got %s.\n",
-            wine_dbgstr_longlong(filter_start_time), wine_dbgstr_longlong(current_time));
+    ok(current_time == filter_start_time, "Expected current time %I64d, got %I64d.\n",
+            filter_start_time, current_time);
 
     clock.time = 23456789;
 
     current_time = 0xdeadbeefdeadbeef;
     hr = IDirectDrawStreamSample_GetSampleTimes(stream_sample, NULL, NULL, &current_time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current_time == filter_start_time + 11111111, "Expected current time %s, got %s.\n",
-            wine_dbgstr_longlong(filter_start_time + 11111111), wine_dbgstr_longlong(current_time));
+    ok(current_time == filter_start_time + 11111111, "Expected current time %I64d, got %I64d.\n",
+            filter_start_time + 11111111, current_time);
 
     start_time = 0xdeadbeefdeadbeef;
     end_time = 0xdeadbeefdeadbeef;
     hr = IDirectDrawStreamSample_GetSampleTimes(stream_sample, &start_time, &end_time, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start_time == 0, "Got start time %s.\n", wine_dbgstr_longlong(start_time));
-    ok(end_time == 0, "Got end time %s.\n", wine_dbgstr_longlong(end_time));
+    ok(start_time == 0, "Got start time %I64d.\n", start_time);
+    ok(end_time == 0, "Got end time %I64d.\n", end_time);
 
     hr = IDirectDrawStreamSample_Update(stream_sample, SSUPDATE_ASYNC, NULL, NULL, 0);
     ok(hr == MS_S_PENDING, "Got hr %#lx.\n", hr);
@@ -10943,8 +10943,8 @@ static void test_ddrawstreamsample_get_sample_times(void)
     end_time = 0xdeadbeefdeadbeef;
     hr = IDirectDrawStreamSample_GetSampleTimes(stream_sample, &start_time, &end_time, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start_time == 12345678, "Got start time %s.\n", wine_dbgstr_longlong(start_time));
-    ok(end_time == 23456789, "Got end time %s.\n", wine_dbgstr_longlong(end_time));
+    ok(start_time == 12345678, "Got start time %I64d.\n", start_time);
+    ok(end_time == 23456789, "Got end time %I64d.\n", end_time);
 
     hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
diff --git a/dlls/bluetoothapis/Makefile.in b/dlls/bluetoothapis/Makefile.in
index aaa85e26f842..7712c2fa5f50 100644
--- a/dlls/bluetoothapis/Makefile.in
+++ b/dlls/bluetoothapis/Makefile.in
@@ -2,8 +2,6 @@ MODULE    = bluetoothapis.dll
 IMPORTLIB = bluetoothapis
 IMPORTS = setupapi cfgmgr32 user32
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	bluetoothapis.rc \
 	gatt.c \
diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c
index f69b39c86582..c53cf69bfac6 100644
--- a/dlls/cfgmgr32/cfgmgr32.c
+++ b/dlls/cfgmgr32/cfgmgr32.c
@@ -1785,6 +1785,39 @@ CONFIGRET WINAPI CM_Locate_DevNodeA( DEVINST *node, DEVINSTID_A instance_id, ULO
     return CM_Locate_DevNode_ExA( node, instance_id, flags, NULL );
 }
 
+/***********************************************************************
+ *           CM_Get_Parent (cfgmgr32.@)
+ */
+CONFIGRET WINAPI CM_Get_Parent( DEVINST *parent, DEVINST node, ULONG flags )
+{
+    return CM_Get_Parent_Ex( parent, node, flags, NULL );
+}
+
+/***********************************************************************
+ *           CM_Get_Parent_Ex (cfgmgr32.@)
+ */
+CONFIGRET WINAPI CM_Get_Parent_Ex( DEVINST *parent, DEVINST node, ULONG flags, HMACHINE machine )
+{
+    WCHAR parent_id[MAX_DEVICE_ID_LEN];
+    DEVPROPTYPE type;
+    struct device dev;
+    struct property prop;
+    DWORD size = sizeof(parent_id);
+
+    TRACE( "parent %p, node %#lx, flags %#lx, machine %p\n", parent, node, flags, machine );
+    if (machine) FIXME( "machine %p not implemented!\n", machine );
+
+    if (!parent) return CR_INVALID_POINTER;
+    *parent = 0;
+
+    if (devnode_get_device( node, &dev )) return CR_INVALID_DEVNODE;
+
+    init_property( &prop, &DEVPKEY_Device_Parent, &type, (BYTE *)parent_id, &size, TRUE );
+    if (get_device_property( HKEY_LOCAL_MACHINE, &dev, &prop )) return CR_NO_SUCH_DEVNODE;
+
+    return CM_Locate_DevNodeW( parent, parent_id, 0 );
+}
+
 /***********************************************************************
  *           CM_Get_Device_ID_Size_Ex (cfgmgr32.@)
  */
@@ -2053,16 +2086,6 @@ CONFIGRET WINAPI CM_Get_Child( DEVINST *child, DEVINST node, ULONG flags )
     return CM_Get_Child_Ex( child, node, flags, NULL );
 }
 
-/***********************************************************************
- *              CM_Get_Parent (cfgmgr32.@)
- */
-DWORD WINAPI CM_Get_Parent( DEVINST *parent, DEVINST child, ULONG flags )
-{
-    FIXME( "parent %p, child %#lx, flags %#lx stub!\n", parent, child, flags );
-    if (parent) *parent = 0;
-    return CR_NO_SUCH_DEVNODE;
-}
-
 /***********************************************************************
  *              CM_Create_DevNodeW  (cfgmgr32.@)
  */
diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec
index df23120954c0..7a0063f23664 100644
--- a/dlls/cfgmgr32/cfgmgr32.spec
+++ b/dlls/cfgmgr32/cfgmgr32.spec
@@ -143,7 +143,7 @@
 @ stub CM_Get_Next_Res_Des
 @ stub CM_Get_Next_Res_Des_Ex
 @ stdcall CM_Get_Parent(ptr long long)
-@ stub CM_Get_Parent_Ex
+@ stdcall CM_Get_Parent_Ex(ptr long long ptr)
 @ stub CM_Get_Res_Des_Data
 @ stub CM_Get_Res_Des_Data_Ex
 @ stub CM_Get_Res_Des_Data_Size
diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c
index 75fb67b7eed8..213eb7f62297 100644
--- a/dlls/cfgmgr32/tests/cfgmgr32.c
+++ b/dlls/cfgmgr32/tests/cfgmgr32.c
@@ -3577,9 +3577,9 @@ static void test_CM_Get_DevNode_Property(void)
     type = 0xdeadbeef;
     len = sizeof(buffer);
     ret = CM_Get_DevNode_PropertyW( node, &DEVPKEY_Device_Parent, &type, (BYTE *)buffer, &len, 0 );
-    todo_wine ok_x4( ret, ==, CR_SUCCESS );
-    todo_wine ok_u4( type, ==, DEVPROP_TYPE_STRING );
-    todo_wine ok_u4( len, >, 1 );
+    ok_x4( ret, ==, CR_SUCCESS );
+    ok_u4( type, ==, DEVPROP_TYPE_STRING );
+    ok_u4( len, >, 1 );
     len = sizeof(buffer);
     ret = CM_Get_DevNode_PropertyW( node, &DEVPKEY_Device_Children, &type, (BYTE *)buffer, &len, 0 );
     ok_x4( ret, ==, CR_NO_SUCH_VALUE );
diff --git a/dlls/combase/combase.c b/dlls/combase/combase.c
index a627d4779ec4..94e256f7cc17 100644
--- a/dlls/combase/combase.c
+++ b/dlls/combase/combase.c
@@ -21,14 +21,17 @@
 #include "ntstatus.h"
 #define USE_COM_CONTEXT_DEF
 #include "objbase.h"
+#include "comsvcs.h"
 #include "ctxtcall.h"
 #include "oleauto.h"
 #include "dde.h"
 #include "winternl.h"
 
+#include "dcom.h"
 #include "combase_private.h"
 
 #include "wine/debug.h"
+#include "wine/exception.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(ole);
 
@@ -395,39 +398,6 @@ HRESULT WINAPI InternalTlsAllocData(struct tlsdata **data)
     return S_OK;
 }
 
-static void com_cleanup_tlsdata(void)
-{
-    struct tlsdata *tlsdata = NtCurrentTeb()->ReservedForOle;
-    struct init_spy *cursor, *cursor2;
-
-    if (!tlsdata)
-        return;
-
-    if (tlsdata->apt)
-        apartment_release(tlsdata->apt);
-    if (tlsdata->implicit_mta_cookie)
-        apartment_decrement_mta_usage(tlsdata->implicit_mta_cookie);
-
-    if (tlsdata->errorinfo)
-        IErrorInfo_Release(tlsdata->errorinfo);
-    if (tlsdata->state)
-        IUnknown_Release(tlsdata->state);
-
-    LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &tlsdata->spies, struct init_spy, entry)
-    {
-        list_remove(&cursor->entry);
-        if (cursor->spy)
-            IInitializeSpy_Release(cursor->spy);
-        free(cursor);
-    }
-
-    if (tlsdata->context_token)
-        IObjContext_Release(tlsdata->context_token);
-
-    free(tlsdata);
-    NtCurrentTeb()->ReservedForOle = NULL;
-}
-
 struct global_options
 {
     IGlobalOptions IGlobalOptions_iface;
@@ -2133,6 +2103,9 @@ HRESULT WINAPI CoWaitForMultipleHandles(DWORD flags, DWORD timeout, ULONG handle
     apt = com_get_current_apt();
     message_loop = apt && !apt->multi_threaded;
 
+    if (message_loop)
+        rpc_start_remoting(apt);
+
     if (flags & COWAIT_WAITALL)
         wait_flags |= MWMO_WAITALL;
     if (flags & COWAIT_ALERTABLE)
@@ -2436,13 +2409,25 @@ HRESULT WINAPI CoRegisterPSClsid(REFIID riid, REFCLSID rclsid)
     return S_OK;
 }
 
-struct thread_context
+static CRITICAL_SECTION context_cs;
+static CRITICAL_SECTION_DEBUG context_cs_debug =
+{
+    0, 0, &context_cs,
+    { &context_cs_debug.ProcessLocksList, &context_cs_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": context_cs") }
+};
+static CRITICAL_SECTION context_cs = { &context_cs_debug, -1, 0, 0, 0, 0 };
+
+
+static struct thread_context
 {
     IComThreadingInfo IComThreadingInfo_iface;
     IContextCallback IContextCallback_iface;
     IObjContext IObjContext_iface;
-    LONG refcount;
-};
+    LONG ref;
+    LONG reported_ref;
+    OXID oxid;
+} *mta_context;
 
 static inline struct thread_context *impl_from_IComThreadingInfo(IComThreadingInfo *iface)
 {
@@ -2492,22 +2477,36 @@ static HRESULT WINAPI thread_context_info_QueryInterface(IComThreadingInfo *ifac
 static ULONG WINAPI thread_context_info_AddRef(IComThreadingInfo *iface)
 {
     struct thread_context *context = impl_from_IComThreadingInfo(iface);
-    return InterlockedIncrement(&context->refcount);
+    InterlockedIncrement(&context->ref);
+    return InterlockedIncrement(&context->reported_ref);
 }
 
-static ULONG WINAPI thread_context_info_Release(IComThreadingInfo *iface)
+static void context_token_release(IObjContext *iface)
 {
-    struct thread_context *context = impl_from_IComThreadingInfo(iface);
+    struct thread_context *context = impl_from_IObjContext(iface);
 
-    /* Context instance is initially created with CoGetContextToken() with refcount set to 0,
-       releasing context while refcount is at 0 destroys it. */
-    if (!context->refcount)
+    if (!InterlockedDecrement(&context->ref))
     {
+        EnterCriticalSection(&context_cs);
+        if (context->ref)
+        {
+            LeaveCriticalSection(&context_cs);
+            return;
+        }
+        if (context == mta_context) mta_context = NULL;
+        LeaveCriticalSection(&context_cs);
+
         free(context);
-        return 0;
     }
+}
 
-    return InterlockedDecrement(&context->refcount);
+static ULONG WINAPI thread_context_info_Release(IComThreadingInfo *iface)
+{
+    struct thread_context *context = impl_from_IComThreadingInfo(iface);
+    ULONG ret = InterlockedDecrement(&context->reported_ref);
+
+    context_token_release(&context->IObjContext_iface);
+    return ret;
 }
 
 static HRESULT WINAPI thread_context_info_GetCurrentApartmentType(IComThreadingInfo *iface, APTTYPE *apttype)
@@ -2553,9 +2552,17 @@ static HRESULT WINAPI thread_context_info_GetCurrentLogicalThreadId(IComThreadin
 
 static HRESULT WINAPI thread_context_info_SetCurrentLogicalThreadId(IComThreadingInfo *iface, REFGUID logical_thread_id)
 {
-    FIXME("%s stub\n", debugstr_guid(logical_thread_id));
+    struct tlsdata *tlsdata;
+    HRESULT hr;
 
-    return E_NOTIMPL;
+    TRACE("%s\n", debugstr_guid(logical_thread_id));
+
+    if (FAILED(hr = com_get_tlsdata(&tlsdata)))
+        return hr;
+
+    tlsdata->causality_id = *logical_thread_id;
+    tlsdata->flags |= OLETLS_UUIDINITIALIZED;
+    return S_OK;
 }
 
 static const IComThreadingInfoVtbl thread_context_info_vtbl =
@@ -2590,9 +2597,93 @@ static ULONG WINAPI thread_context_callback_Release(IContextCallback *iface)
 static HRESULT WINAPI thread_context_callback_ContextCallback(IContextCallback *iface,
         PFNCONTEXTCALL callback, ComCallData *param, REFIID riid, int method, IUnknown *punk)
 {
-    FIXME("%p, %p, %p, %s, %d, %p\n", iface, callback, param, debugstr_guid(riid), method, punk);
+    struct thread_context *context = impl_from_IContextCallback(iface);
+    IObjContext *current_context;
+    XAptCallback apt_callback;
+    struct apartment *apt;
+    STDOBJREF stdobjref;
+    OXID_INFO oxid_info;
+    IRundown *rundown;
+    HRESULT hr;
 
-    return E_NOTIMPL;
+    TRACE("%p, %p, %p, %s, %d, %p\n", iface, callback, param, debugstr_guid(riid), method, punk);
+
+    if (!callback)
+        return E_INVALIDARG;
+
+    hr = CoGetContextToken((ULONG_PTR *)&current_context);
+    if (FAILED(hr))
+        return hr;
+
+    if (!(apt = apartment_get_current_or_mta()))
+    {
+        ERR("apartment not initialised\n");
+        return CO_E_NOTINITIALIZED;
+    }
+    rpc_start_remoting(apt);
+
+    if (&context->IObjContext_iface == current_context)
+    {
+        IComThreadingInfo *cti = NULL;
+        GUID thread_id;
+
+        apartment_release(apt);
+
+        if (IsEqualIID(riid, &IID_IEnterActivityWithNoLock))
+        {
+            cti = &context->IComThreadingInfo_iface;
+            if (FAILED((hr = IComThreadingInfo_GetCurrentLogicalThreadId(cti, &thread_id))))
+                return hr;
+            if (FAILED((hr = IComThreadingInfo_SetCurrentLogicalThreadId(cti, riid))))
+                return hr;
+        }
+
+        __TRY
+        {
+            hr = callback(param);
+        }
+        __EXCEPT_ALL
+        {
+            hr = RPC_E_SERVERFAULT;
+        }
+        __ENDTRY
+
+        if (cti)
+            IComThreadingInfo_SetCurrentLogicalThreadId(cti, &thread_id);
+
+        TRACE("callback returned %lx\n", hr);
+        return hr;
+    }
+
+    hr = rpc_resolve_oxid(context->oxid, &oxid_info);
+    if (SUCCEEDED(hr))
+    {
+        stdobjref.flags = SORFP_NOLIFETIMEMGMT | SORF_NOPING;
+        stdobjref.cPublicRefs = 1;
+        stdobjref.oxid = context->oxid;
+        stdobjref.oid = -1;
+        stdobjref.ipid = oxid_info.ipidRemUnknown;
+        hr = unmarshal_object(&stdobjref, apt, MSHCTX_INPROC, NULL, &IID_IRundown, &oxid_info, (void**)&rundown);
+        apartment_release(apt);
+    }
+    if (FAILED(hr))
+        return hr;
+
+    apt_callback.pfnCallback = (ULONG_PTR)callback;
+    apt_callback.pParam = (ULONG_PTR)param;
+    apt_callback.pServerCtx = (ULONG_PTR)&context->IObjContext_iface;
+    apt_callback.pUnk = (ULONG_PTR)punk;
+    apt_callback.iid = *riid;
+    apt_callback.iMethod = method;
+    get_process_secret(&apt_callback.guidProcessSecret);
+    if (IsEqualIID(riid, &IID_ICallbackWithNoReentrancyToApplicationSTA))
+        hr = IRundown_DoNonreentrantCallback(rundown, &apt_callback);
+    else
+        hr = IRundown_DoCallback(rundown, &apt_callback);
+    IRundown_Release(rundown);
+
+    TRACE("callback returned %lx\n", hr);
+    return hr;
 }
 
 static const IContextCallbackVtbl thread_context_callback_vtbl =
@@ -2708,21 +2799,44 @@ static const IObjContextVtbl thread_object_context_vtbl =
 HRESULT WINAPI CoGetContextToken(ULONG_PTR *token)
 {
     struct tlsdata *tlsdata;
+    struct apartment *apt;
     HRESULT hr;
 
     TRACE("%p\n", token);
 
-    if (!InternalIsProcessInitialized())
+    if (FAILED(hr = com_get_tlsdata(&tlsdata)))
+        return hr;
+
+    if (!token)
+        return E_POINTER;
+
+    if (!(apt = apartment_get_current_or_mta()))
     {
         ERR("apartment not initialised\n");
         return CO_E_NOTINITIALIZED;
     }
 
-    if (FAILED(hr = com_get_tlsdata(&tlsdata)))
-        return hr;
+    if (tlsdata->context_token)
+    {
+        struct thread_context *context = impl_from_IObjContext(tlsdata->context_token);
+        if ((apt->multi_threaded && context != mta_context) ||
+            (!apt->multi_threaded && context == mta_context))
+        {
+            context_token_release(tlsdata->context_token);
+            tlsdata->context_token = NULL;
+        }
+    }
 
-    if (!token)
-        return E_POINTER;
+    if (!tlsdata->context_token && apt->multi_threaded && mta_context)
+    {
+        EnterCriticalSection(&context_cs);
+        if (mta_context)
+        {
+            tlsdata->context_token = &mta_context->IObjContext_iface;
+            InterlockedIncrement(&mta_context->ref);
+        }
+        LeaveCriticalSection(&context_cs);
+    }
 
     if (!tlsdata->context_token)
     {
@@ -2730,14 +2844,31 @@ HRESULT WINAPI CoGetContextToken(ULONG_PTR *token)
 
         context = calloc(1, sizeof(*context));
         if (!context)
+        {
+            apartment_release(apt);
             return E_OUTOFMEMORY;
+        }
 
         context->IComThreadingInfo_iface.lpVtbl = &thread_context_info_vtbl;
         context->IContextCallback_iface.lpVtbl = &thread_context_callback_vtbl;
         context->IObjContext_iface.lpVtbl = &thread_object_context_vtbl;
-        /* Context token does not take a reference, it's always zero until the
-           interface is explicitly requested with CoGetObjectContext(). */
-        context->refcount = 0;
+        context->ref = 1;
+        context->reported_ref = 0;
+        context->oxid = apt->oxid;
+
+        if (apt->multi_threaded)
+        {
+            EnterCriticalSection(&context_cs);
+            if (!mta_context)
+                mta_context = context;
+            else
+            {
+                context_token_release(&context->IObjContext_iface);
+                context = mta_context;
+                InterlockedIncrement(&context->ref);
+            }
+            LeaveCriticalSection(&context_cs);
+        }
 
         tlsdata->context_token = &context->IObjContext_iface;
     }
@@ -2745,6 +2876,7 @@ HRESULT WINAPI CoGetContextToken(ULONG_PTR *token)
     *token = (ULONG_PTR)tlsdata->context_token;
     TRACE("context_token %p\n", tlsdata->context_token);
 
+    apartment_release(apt);
     return S_OK;
 }
 
@@ -2762,7 +2894,7 @@ HRESULT WINAPI CoGetCurrentLogicalThreadId(GUID *id)
     if (FAILED(hr = com_get_tlsdata(&tlsdata)))
         return hr;
 
-    if (IsEqualGUID(&tlsdata->causality_id, &GUID_NULL))
+    if (!(tlsdata->flags & OLETLS_UUIDINITIALIZED))
     {
         CoCreateGuid(&tlsdata->causality_id);
         tlsdata->flags |= OLETLS_UUIDINITIALIZED;
@@ -3435,6 +3567,39 @@ HRESULT WINAPI CoRegisterActivationFilter(IActivationFilter *filter)
     return E_NOTIMPL;
 }
 
+static void com_cleanup_tlsdata(void)
+{
+    struct tlsdata *tlsdata = NtCurrentTeb()->ReservedForOle;
+    struct init_spy *cursor, *cursor2;
+
+    if (!tlsdata)
+        return;
+
+    if (tlsdata->apt)
+        apartment_release(tlsdata->apt);
+    if (tlsdata->implicit_mta_cookie)
+        apartment_decrement_mta_usage(tlsdata->implicit_mta_cookie);
+
+    if (tlsdata->errorinfo)
+        IErrorInfo_Release(tlsdata->errorinfo);
+    if (tlsdata->state)
+        IUnknown_Release(tlsdata->state);
+
+    LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &tlsdata->spies, struct init_spy, entry)
+    {
+        list_remove(&cursor->entry);
+        if (cursor->spy)
+            IInitializeSpy_Release(cursor->spy);
+        free(cursor);
+    }
+
+    if (tlsdata->context_token)
+        context_token_release(tlsdata->context_token);
+
+    free(tlsdata);
+    NtCurrentTeb()->ReservedForOle = NULL;
+}
+
 /***********************************************************************
  *            DllMain     (combase.@)
  */
diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h
index ca79356e8eb3..76393650f3a9 100644
--- a/dlls/combase/combase_private.h
+++ b/dlls/combase/combase_private.h
@@ -174,8 +174,14 @@ void apartment_revoke_all_classes(const struct apartment *apt);
 struct apartment * apartment_findfromoxid(OXID oxid);
 struct apartment * apartment_findfromtid(DWORD tid);
 
+/* private flag indicating that the caller does not want to notify the stub
+ * when the proxy disconnects or is destroyed */
+#define SORFP_NOLIFETIMEMGMT SORF_OXRES2
+
 HRESULT marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object,
         DWORD dest_context, void *dest_context_data, MSHLFLAGS mshlflags);
+HRESULT unmarshal_object(const STDOBJREF *stdobjref, struct apartment *apt, MSHCTX dest_context,
+        void *dest_context_data, REFIID riid, const OXID_INFO *oxid_info, void **object);
 
 /* Stub Manager */
 
@@ -261,3 +267,4 @@ HRESULT ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt,
         IID *iid, IUnknown **iface);
 HRESULT ipid_get_dest_context(const IPID *ipid, MSHCTX *dest_context, void **dest_context_data);
 HRESULT start_apartment_remote_unknown(struct apartment *apt);
+void get_process_secret(GUID *process_secret);
diff --git a/dlls/combase/dcom.idl b/dlls/combase/dcom.idl
index 56e3152456c4..84b7ab346ecf 100644
--- a/dlls/combase/dcom.idl
+++ b/dlls/combase/dcom.idl
@@ -21,6 +21,7 @@
 
 #pragma makedep header
 
+#include "hstring.idl"
 #include "wine/orpc.idl"
 
 [
@@ -60,3 +61,74 @@ interface IRemUnknown : IUnknown
         [in] unsigned short cInterfaceRefs,
         [in, size_is(cInterfaceRefs)] REMINTERFACEREF *InterfaceRefs);
 }
+
+[
+    object,
+    uuid(00000142-0000-0000-C000-000000000046)
+]
+interface IRemUnknown2 : IRemUnknown
+{
+    typedef [unique] IRemUnknown2 *LPREMUNKNOWN2;
+
+    HRESULT RemQueryInterface2(
+        [in] REFIPID ripid,
+        [in] unsigned short cIids,
+        [in, size_is(cIids)] IID *iids,
+        [out, size_is(cIids)] HRESULT *phr,
+        [out, size_is(cIids)] MInterfacePointer **ppMIF);
+}
+
+[
+    object,
+    uuid(0000013C-0000-0000-C000-000000000046)
+]
+interface IRemUnknownN : IRemUnknown2
+{
+    const DWORD IRUF_CONVERTTOWEAK = 0x01;
+    const DWORD IRUF_CONVERTTOSTRONG = 0x02;
+    const DWORD IRUF_DISCONNECTIFLASTSTRONG = 0x04;
+
+    typedef struct tagXAptCallback
+    {
+        DWORD64 pfnCallback;
+        DWORD64 pParam;
+        DWORD64 pServerCtx;
+        DWORD64 pUnk;
+        GUID iid;
+        INT iMethod;
+        GUID guidProcessSecret;
+    } XAptCallback;
+
+    HRESULT AcknowledgeMarshalingSets(
+        [in] WORD cMarshalingSets,
+        [in, size_is(cMarshalingSets)] DWORD64 *pMarshalingSets);
+
+    HRESULT RemChangeRef(
+        [in] DWORD flags,
+        [in] WORD cInterfaceRefs,
+        [in, size_is(cInterfaceRefs)] REMINTERFACEREF InterfaceRefs[]);
+
+    HRESULT DoCallback(
+        [in] XAptCallback *pCallbackData);
+
+    HRESULT DoNonreentrantCallback(
+        [in] XAptCallback *pCallbackData);
+
+    HRESULT GetInterfaceNameFromIPID(
+        [in] REFIPID ripid,
+        [out] HSTRING *interfaceName);
+}
+
+[
+    object,
+    uuid(00000134-0000-0000-C000-000000000046),
+]
+interface IRundown : IRemUnknownN
+{
+    const DWORD MAX_OID_RUNDOWNS_PER_CALL = 100;
+
+    HRESULT RundownOid(
+        [in, range(1, MAX_OID_RUNDOWNS_PER_CALL)] DWORD cOid,
+        [in, size_is(cOid)] OID aOid[],
+        [out, size_is(cOid)] BYTE aRundownStatus[]);
+}
diff --git a/dlls/combase/marshal.c b/dlls/combase/marshal.c
index f638604f5a33..6e3d35ce580d 100644
--- a/dlls/combase/marshal.c
+++ b/dlls/combase/marshal.c
@@ -36,19 +36,11 @@ HRESULT WINAPI RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid,
                                 DWORD dest_context, void *dest_context_data,
                                 IRpcChannelBuffer **chan, struct apartment *apt);
 
-static HRESULT unmarshal_object(const STDOBJREF *stdobjref, struct apartment *apt,
-                                MSHCTX dest_context, void *dest_context_data,
-                                REFIID riid, const OXID_INFO *oxid_info,
-                                void **object);
-
 /* number of refs given out for normal marshaling */
 #define NORMALEXTREFS 5
 
 /* private flag indicating that the object was marshaled as table-weak */
 #define SORFP_TABLEWEAK SORF_OXRES1
-/* private flag indicating that the caller does not want to notify the stub
- * when the proxy disconnects or is destroyed */
-#define SORFP_NOLIFETIMEMGMT SORF_OXRES2
 
 /* imported interface proxy */
 struct ifproxy
@@ -1873,7 +1865,7 @@ static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnk
 
         /* do the unmarshal */
         hr = unmarshal_object(&stdobjref, apt, This->dest_context,
-                              This->dest_context_data, &IID_IRemUnknown,
+                              This->dest_context_data, &IID_IRundown,
                               &This->oxid_info, (void**)remunk);
         if (hr == S_OK && called_in_original_apt)
         {
@@ -2095,7 +2087,7 @@ static HRESULT WINAPI StdMarshalImpl_MarshalInterface(IMarshal *iface, IStream *
 /* helper for StdMarshalImpl_UnmarshalInterface - does the unmarshaling with
  * no questions asked about the rules surrounding same-apartment unmarshals
  * and table marshaling */
-static HRESULT unmarshal_object(const STDOBJREF *stdobjref, struct apartment *apt, MSHCTX dest_context,
+HRESULT unmarshal_object(const STDOBJREF *stdobjref, struct apartment *apt, MSHCTX dest_context,
         void *dest_context_data, REFIID riid, const OXID_INFO *oxid_info, void **object)
 {
     struct proxy_manager *proxy_manager = NULL;
diff --git a/dlls/combase/stubmanager.c b/dlls/combase/stubmanager.c
index 566f92c8a752..4756e334f9f9 100644
--- a/dlls/combase/stubmanager.c
+++ b/dlls/combase/stubmanager.c
@@ -32,7 +32,9 @@
 #include "windef.h"
 #include "winbase.h"
 #include "winuser.h"
+#define USE_COM_CONTEXT_DEF
 #include "objbase.h"
+#include "comsvcs.h"
 #include "rpc.h"
 
 #include "wine/debug.h"
@@ -638,50 +640,36 @@ BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid)
 
 /*****************************************************************************
  *
- * IRemUnknown implementation
+ * IRundown / IRemUnknown implementation
  *
  *
  * Note: this object is not related to the lifetime of a stub_manager, but it
  * interacts with stub managers.
  */
 
-typedef struct rem_unknown
+typedef struct
 {
-    IRemUnknown IRemUnknown_iface;
+    IRundown IRundown_iface;
     LONG refs;
-} RemUnknown;
+} Rundown;
 
-static const IRemUnknownVtbl RemUnknown_Vtbl;
-
-static inline RemUnknown *impl_from_IRemUnknown(IRemUnknown *iface)
-{
-    return CONTAINING_RECORD(iface, RemUnknown, IRemUnknown_iface);
-}
-
-/* construct an IRemUnknown object with one outstanding reference */
-static HRESULT RemUnknown_Construct(IRemUnknown **ppRemUnknown)
+static inline Rundown *impl_from_IRundown(IRundown *iface)
 {
-    RemUnknown *object = malloc(sizeof(*object));
-
-    if (!object)
-        return E_OUTOFMEMORY;
-
-    object->IRemUnknown_iface.lpVtbl = &RemUnknown_Vtbl;
-    object->refs = 1;
-
-    *ppRemUnknown = &object->IRemUnknown_iface;
-    return S_OK;
+    return CONTAINING_RECORD(iface, Rundown, IRundown_iface);
 }
 
-static HRESULT WINAPI RemUnknown_QueryInterface(IRemUnknown *iface, REFIID riid, void **ppv)
+static HRESULT WINAPI Rundown_QueryInterface(IRundown *iface, REFIID riid, void **ppv)
 {
     TRACE("%p, %s, %p\n", iface, debugstr_guid(riid), ppv);
 
     if (IsEqualIID(riid, &IID_IUnknown) ||
-        IsEqualIID(riid, &IID_IRemUnknown))
+        IsEqualIID(riid, &IID_IRemUnknown) ||
+        IsEqualIID(riid, &IID_IRemUnknown2) ||
+        IsEqualIID(riid, &IID_IRemUnknownN) ||
+        IsEqualIID(riid, &IID_IRundown))
     {
         *ppv = iface;
-        IRemUnknown_AddRef(iface);
+        IRundown_AddRef(iface);
         return S_OK;
     }
 
@@ -692,31 +680,31 @@ static HRESULT WINAPI RemUnknown_QueryInterface(IRemUnknown *iface, REFIID riid,
     return E_NOINTERFACE;
 }
 
-static ULONG WINAPI RemUnknown_AddRef(IRemUnknown *iface)
+static ULONG WINAPI Rundown_AddRef(IRundown *iface)
 {
     ULONG refs;
-    RemUnknown *remunk = impl_from_IRemUnknown(iface);
+    Rundown *rundown = impl_from_IRundown(iface);
 
-    refs = InterlockedIncrement(&remunk->refs);
+    refs = InterlockedIncrement(&rundown->refs);
 
     TRACE("%p before: %ld\n", iface, refs-1);
     return refs;
 }
 
-static ULONG WINAPI RemUnknown_Release(IRemUnknown *iface)
+static ULONG WINAPI Rundown_Release(IRundown *iface)
 {
     ULONG refs;
-    RemUnknown *remunk = impl_from_IRemUnknown(iface);
+    Rundown *rundown = impl_from_IRundown(iface);
 
-    refs = InterlockedDecrement(&remunk->refs);
+    refs = InterlockedDecrement(&rundown->refs);
     if (!refs)
-        free(remunk);
+        free(rundown);
 
     TRACE("%p after: %ld\n", iface, refs);
     return refs;
 }
 
-static HRESULT WINAPI RemUnknown_RemQueryInterface(IRemUnknown *iface,
+static HRESULT WINAPI Rundown_RemQueryInterface(IRundown *iface,
     REFIPID ripid, ULONG cRefs, USHORT cIids, IID *iids /* [size_is(cIids)] */,
     REMQIRESULT **ppQIResults /* [size_is(,cIids)] */)
 {
@@ -758,7 +746,7 @@ static HRESULT WINAPI RemUnknown_RemQueryInterface(IRemUnknown *iface,
         return S_FALSE; /* we got some interfaces */
 }
 
-static HRESULT WINAPI RemUnknown_RemAddRef(IRemUnknown *iface,
+static HRESULT WINAPI Rundown_RemAddRef(IRundown *iface,
     USHORT cInterfaceRefs,
     REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */,
     HRESULT *pResults /* [size_is(cInterfaceRefs)] */)
@@ -791,7 +779,7 @@ static HRESULT WINAPI RemUnknown_RemAddRef(IRemUnknown *iface,
     return hr;
 }
 
-static HRESULT WINAPI RemUnknown_RemRelease(IRemUnknown *iface,
+static HRESULT WINAPI Rundown_RemRelease(IRundown *iface,
     USHORT cInterfaceRefs,
     REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */)
 {
@@ -824,35 +812,160 @@ static HRESULT WINAPI RemUnknown_RemRelease(IRemUnknown *iface,
     return hr;
 }
 
-static const IRemUnknownVtbl RemUnknown_Vtbl =
+static HRESULT WINAPI Rundown_RemQueryInterface2(IRundown *iface, REFIPID ripid,
+        unsigned short cIids, IID *iids, HRESULT *phr, MInterfacePointer **ppMIF)
+{
+    FIXME("%p, %p, %hu, %p, %p, %p\n", iface, ripid, cIids, iids, phr, ppMIF);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Rundown_AcknowledgeMarshalingSets(IRundown *iface,
+        WORD cMarshalingSets, DWORD64 *pMarshalingSets)
+{
+    FIXME("%p, %hu, %p\n", iface, cMarshalingSets, pMarshalingSets);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Rundown_RemChangeRef(IRundown *iface, DWORD flags,
+        WORD cInterfaceRefs, REMINTERFACEREF InterfaceRefs[])
+{
+    FIXME("%p, %lu, %hu, %p\n", iface, flags, cInterfaceRefs, InterfaceRefs);
+    return E_NOTIMPL;
+}
+
+static BOOL WINAPI init_process_secret(PINIT_ONCE init_once, void *param, void **ctx)
+{
+    GUID *secret = param;
+    return SUCCEEDED(UuidCreate(secret));
+}
+
+void get_process_secret(GUID *process_secret)
+{
+    static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
+    static GUID secret;
+
+    InitOnceExecuteOnce(&init_once, init_process_secret, &secret, NULL);
+    *process_secret = secret;
+}
+
+static HRESULT WINAPI Rundown_DoCallback(IRundown *iface, XAptCallback *pCallbackData)
+{
+    HRESULT (WINAPI *callback)(void *) = (void*)(ULONG_PTR)pCallbackData->pfnCallback;
+    void *param = (void *)(ULONG_PTR)pCallbackData->pParam;
+    IComThreadingInfo *cti = NULL;
+    GUID thread_id, secret;
+    IObjContext *context;
+    HRESULT hr;
+
+    TRACE("%p, %p\n", iface, pCallbackData);
+
+    get_process_secret(&secret);
+    if (!IsEqualIID(&secret, &pCallbackData->guidProcessSecret))
+        return E_FAIL;
+    if (FAILED((hr = CoGetContextToken((ULONG_PTR *)&context))))
+        return hr;
+    if (pCallbackData->pServerCtx != (ULONG_PTR)context)
+    {
+        ERR("context token doesn't match\n");
+        return E_FAIL;
+    }
+
+    if (IsEqualIID(&IID_IEnterActivityWithNoLock, &pCallbackData->iid))
+    {
+        hr = IObjContext_QueryInterface(context, &IID_IComThreadingInfo, (void **)&cti);
+        if (FAILED(hr))
+            return hr;
+        hr = IComThreadingInfo_GetCurrentLogicalThreadId(cti, &thread_id);
+        if (SUCCEEDED(hr))
+            hr = IComThreadingInfo_SetCurrentLogicalThreadId(cti, &IID_IEnterActivityWithNoLock);
+        if (FAILED(hr))
+        {
+            IComThreadingInfo_Release(cti);
+            return hr;
+        }
+    }
+
+    hr = callback(param);
+
+    if (cti)
+    {
+        IComThreadingInfo_SetCurrentLogicalThreadId(cti, &thread_id);
+        IComThreadingInfo_Release(cti);
+    }
+    return hr;
+}
+
+static HRESULT WINAPI Rundown_DoNonreentrantCallback(IRundown *iface, XAptCallback *pCallbackData)
+{
+    FIXME("%p, %p\n", iface, pCallbackData);
+    return IRundown_DoCallback(iface, pCallbackData);
+}
+
+static HRESULT WINAPI Rundown_GetInterfaceNameFromIPID(IRundown *iface,
+        REFIPID ripid, HSTRING *interfaceName)
+{
+    FIXME("%p, %p %p\n", iface, ripid, interfaceName);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Rundown_RundownOid(IRundown *iface,
+        DWORD cOid, OID aOid[], BYTE aRundownStatus[])
 {
-    RemUnknown_QueryInterface,
-    RemUnknown_AddRef,
-    RemUnknown_Release,
-    RemUnknown_RemQueryInterface,
-    RemUnknown_RemAddRef,
-    RemUnknown_RemRelease
+    FIXME("%p, %lu, %p %p\n", iface, cOid, aOid, aRundownStatus);
+    return E_NOTIMPL;
+}
+
+static const IRundownVtbl Rundown_Vtbl =
+{
+    Rundown_QueryInterface,
+    Rundown_AddRef,
+    Rundown_Release,
+    Rundown_RemQueryInterface,
+    Rundown_RemAddRef,
+    Rundown_RemRelease,
+    Rundown_RemQueryInterface2,
+    Rundown_AcknowledgeMarshalingSets,
+    Rundown_RemChangeRef,
+    Rundown_DoCallback,
+    Rundown_DoNonreentrantCallback,
+    Rundown_GetInterfaceNameFromIPID,
+    Rundown_RundownOid
 };
 
+/* construct an IRundown object with one outstanding reference */
+static HRESULT Rundown_Construct(IRundown **rundown)
+{
+    Rundown *object = malloc(sizeof(*object));
+
+    if (!object)
+        return E_OUTOFMEMORY;
+
+    object->IRundown_iface.lpVtbl = &Rundown_Vtbl;
+    object->refs = 1;
+
+    *rundown = &object->IRundown_iface;
+    return S_OK;
+}
+
 /* starts the IRemUnknown listener for the current apartment */
 HRESULT start_apartment_remote_unknown(struct apartment *apt)
 {
-    IRemUnknown *pRemUnknown;
+    IRundown *pRundown;
     HRESULT hr = S_OK;
 
     EnterCriticalSection(&apt->cs);
     if (!apt->remunk_exported)
     {
-        /* create the IRemUnknown object */
-        hr = RemUnknown_Construct(&pRemUnknown);
+        /* create the IRundown object */
+        hr = Rundown_Construct(&pRundown);
         if (hr == S_OK)
         {
             STDOBJREF stdobjref; /* dummy - not used */
             /* register it with the stub manager */
-            hr = marshal_object(apt, &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown,
+            hr = marshal_object(apt, &stdobjref, &IID_IRundown, (IUnknown *)pRundown,
                     MSHCTX_DIFFERENTMACHINE, NULL, MSHLFLAGS_NORMAL|MSHLFLAGSP_REMUNKNOWN);
             /* release our reference to the object as the stub manager will manage the life cycle for us */
-            IRemUnknown_Release(pRemUnknown);
+            IRundown_Release(pRundown);
             if (hr == S_OK)
                 apt->remunk_exported = TRUE;
         }
diff --git a/dlls/comctl32/tests/misc.c b/dlls/comctl32/tests/misc.c
index be31bedc848e..83dc93eaed33 100644
--- a/dlls/comctl32/tests/misc.c
+++ b/dlls/comctl32/tests/misc.c
@@ -394,6 +394,42 @@ static void test_LoadIconWithScaleDown(void)
     FreeLibrary(hinst);
 }
 
+static WNDPROC real_class_wndproc;
+static const char *real_class_str;
+
+static LRESULT WINAPI test_real_class_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
+{
+    LRESULT lr = 0;
+
+    /*
+     * Passing through WM_GETTEXT to the WC_IPADDRESSA procedure will cause an
+     * access violation on Windows 7 64-bit.
+     */
+    if ((msg == WM_GETTEXT) && !strcmp( real_class_str, WC_IPADDRESSA )) return lr;
+
+    lr = CallWindowProcA( real_class_wndproc, hwnd, msg, wparam, lparam );
+    if (msg == WM_NCCREATE) lr = 1;
+
+    return lr;
+}
+
+#define check_real_class_name( a, b ) check_real_class_name_( __LINE__, a, b )
+static void check_real_class_name_( int line, HWND hwnd, const char *expect )
+{
+    WCHAR expectW[256], nameW[256];
+    char nameA[256];
+    ULONG len;
+
+    len = RealGetWindowClassA( hwnd, nameA, ARRAY_SIZE(nameA) );
+    ok_(__FILE__, line)( !strcmp( nameA, expect ), "got %s\n", nameA );
+    ok_(__FILE__, line)( len == strlen( expect ), "got %ld\n", len );
+
+    MultiByteToWideChar( CP_ACP, 0, expect, -1, expectW, ARRAY_SIZE(expectW));
+    len = RealGetWindowClassW( hwnd, nameW, ARRAY_SIZE(nameW));
+    ok_(__FILE__, line)( !wcscmp( nameW, expectW ), "got %s\n", debugstr_w(nameW));
+    ok_(__FILE__, line)( len == wcslen( expectW ), "got %ld\n", len );
+}
+
 static void check_class( const char *name, int must_exist, UINT style, UINT ignore, BOOL v6, DWORD classnameidx, BOOL classnameidx_todo )
 {
     WNDCLASSA wc;
@@ -431,6 +467,21 @@ static void check_class( const char *name, int must_exist, UINT style, UINT igno
             name, objid, classnameidx);
 
         DestroyWindow(hwnd);
+
+        real_class_wndproc = wc.lpfnWndProc;
+        real_class_str = name;
+        wc.lpfnWndProc = test_real_class_wndproc;
+        wc.hInstance = GetModuleHandleA( NULL );
+        wc.lpszClassName = "WineTest Class";
+        RegisterClassA( &wc );
+
+        hwnd = CreateWindowA( wc.lpszClassName, 0, 0, 0, 0, 0, 0, 0, NULL, GetModuleHandleA( NULL ), 0 );
+        check_real_class_name( hwnd, wc.lpszClassName );
+
+        DestroyWindow( hwnd );
+        UnregisterClassA( wc.lpszClassName, GetModuleHandleA( NULL ) );
+        real_class_wndproc = NULL;
+        real_class_str = NULL;
     }
     else
         ok( !must_exist, "System class %s does not exist\n", name );
diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c
index 085174561129..c37432822a20 100644
--- a/dlls/crypt32/cert.c
+++ b/dlls/crypt32/cert.c
@@ -880,7 +880,7 @@ static BOOL CertContext_SetProperty(cert_t *cert, DWORD dwPropId,
                 break;
             }
             keyContext.cbSize = sizeof(keyContext);
-            keyContext.hCryptProv = *(const HCRYPTPROV *)pvData;
+            keyContext.hNCryptKey = (NCRYPT_KEY_HANDLE)pvData;
             keyContext.dwKeySpec = CERT_NCRYPT_KEY_SPEC;
             ret = CertContext_SetKeyContextProperty(cert->base.properties, &keyContext);
             break;
diff --git a/dlls/crypt32/pfx.c b/dlls/crypt32/pfx.c
index 3a86fce3165c..b7419e4f446e 100644
--- a/dlls/crypt32/pfx.c
+++ b/dlls/crypt32/pfx.c
@@ -385,7 +385,7 @@ BOOL WINAPI PFXExportCertStoreEx( HCERTSTORE store, CRYPT_DATA_BLOB *pfx, const
         else if (key_ctx.dwKeySpec == CERT_NCRYPT_KEY_SPEC)
         {
             /* Query key blob size first. */
-            sec_status = NCryptExportKey( key_ctx.hCryptProv, 0, BCRYPT_RSAFULLPRIVATE_BLOB, NULL,
+            sec_status = NCryptExportKey( key_ctx.hNCryptKey, 0, BCRYPT_RSAFULLPRIVATE_BLOB, NULL,
                                           NULL, 0, &key_blob_size, 0 );
             if (sec_status)
             {
@@ -403,7 +403,7 @@ BOOL WINAPI PFXExportCertStoreEx( HCERTSTORE store, CRYPT_DATA_BLOB *pfx, const
                 return FALSE;
             }
 
-            sec_status = NCryptExportKey( key_ctx.hCryptProv, 0, BCRYPT_RSAFULLPRIVATE_BLOB, NULL,
+            sec_status = NCryptExportKey( key_ctx.hNCryptKey, 0, BCRYPT_RSAFULLPRIVATE_BLOB, NULL,
                                           key_blob, key_blob_size, &key_blob_size, 0 );
             if (sec_status)
             {
diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c
index f8d1c37b63c4..4771be721b78 100644
--- a/dlls/crypt32/tests/cert.c
+++ b/dlls/crypt32/tests/cert.c
@@ -549,17 +549,17 @@ static void testCertProperties(void)
         HCRYPTPROV_OR_NCRYPT_KEY_HANDLE retrievedHandle = 0;
 
         ret = CertSetCertificateContextProperty(context,
-         CERT_NCRYPT_KEY_HANDLE_PROP_ID, 0, &ncryptHandle);
+         CERT_NCRYPT_KEY_HANDLE_PROP_ID, 0, (void *)ncryptHandle);
         ok(ret, "CertSetCertificateContextProperty failed: %08lx\n", GetLastError());
 
         /* Verify the key context was set with CERT_NCRYPT_KEY_SPEC */
         size = sizeof(keyContext);
-        keyContext.hCryptProv = keyContext.dwKeySpec = 0;
+        keyContext.hNCryptKey = keyContext.dwKeySpec = 0;
         ret = CertGetCertificateContextProperty(context,
          CERT_KEY_CONTEXT_PROP_ID, &keyContext, &size);
         ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError());
-        ok(keyContext.hCryptProv != 0,
-         "Expected non-zero hCryptProv, got 0\n");
+        ok(keyContext.hNCryptKey == ncryptHandle,
+         "Expected key context to now be hNCryptKey, got something else\n");
         ok(keyContext.dwKeySpec == CERT_NCRYPT_KEY_SPEC,
          "Expected dwKeySpec CERT_NCRYPT_KEY_SPEC, got %lx\n", keyContext.dwKeySpec);
 
@@ -568,8 +568,8 @@ static void testCertProperties(void)
         ret = CertGetCertificateContextProperty(context,
          CERT_NCRYPT_KEY_HANDLE_PROP_ID, &retrievedHandle, &size);
         ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError());
-        ok(retrievedHandle == keyContext.hCryptProv,
-         "Expected handle %Ix, got %Ix\n", keyContext.hCryptProv, retrievedHandle);
+        ok(retrievedHandle == keyContext.hNCryptKey,
+         "Expected handle %Ix, got %Ix\n", keyContext.hNCryptKey, retrievedHandle);
         ok(size == sizeof(retrievedHandle),
          "Expected size %Iu, got %lu\n", sizeof(retrievedHandle), size);
 
diff --git a/dlls/d3dcompiler_33/Makefile.in b/dlls/d3dcompiler_33/Makefile.in
index 5753a0a30151..da851b304b61 100644
--- a/dlls/d3dcompiler_33/Makefile.in
+++ b/dlls/d3dcompiler_33/Makefile.in
@@ -4,8 +4,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=33
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,18,904,15
 
diff --git a/dlls/d3dcompiler_34/Makefile.in b/dlls/d3dcompiler_34/Makefile.in
index 7b153d5fbbd2..ced563f9e5cb 100644
--- a/dlls/d3dcompiler_34/Makefile.in
+++ b/dlls/d3dcompiler_34/Makefile.in
@@ -4,8 +4,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=34
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,19,949,46
 
diff --git a/dlls/d3dcompiler_35/Makefile.in b/dlls/d3dcompiler_35/Makefile.in
index aeff9d45c809..4ee53daa6f1a 100644
--- a/dlls/d3dcompiler_35/Makefile.in
+++ b/dlls/d3dcompiler_35/Makefile.in
@@ -4,8 +4,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=35
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,19,949,1104
 
diff --git a/dlls/d3dcompiler_36/Makefile.in b/dlls/d3dcompiler_36/Makefile.in
index 9ec957705ab5..714357b40a6b 100644
--- a/dlls/d3dcompiler_36/Makefile.in
+++ b/dlls/d3dcompiler_36/Makefile.in
@@ -4,8 +4,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=36
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,19,949,2111
 
diff --git a/dlls/d3dcompiler_37/Makefile.in b/dlls/d3dcompiler_37/Makefile.in
index 60f2597d13c5..3052b3a0221f 100644
--- a/dlls/d3dcompiler_37/Makefile.in
+++ b/dlls/d3dcompiler_37/Makefile.in
@@ -4,8 +4,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=37
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,22,949,2248
 
diff --git a/dlls/d3dcompiler_38/Makefile.in b/dlls/d3dcompiler_38/Makefile.in
index 11e680c02e0b..219bd46e4896 100644
--- a/dlls/d3dcompiler_38/Makefile.in
+++ b/dlls/d3dcompiler_38/Makefile.in
@@ -4,8 +4,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=38
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,23,949,2378
 
diff --git a/dlls/d3dcompiler_39/Makefile.in b/dlls/d3dcompiler_39/Makefile.in
index b085a8ee6820..803dd6fe0a54 100644
--- a/dlls/d3dcompiler_39/Makefile.in
+++ b/dlls/d3dcompiler_39/Makefile.in
@@ -5,8 +5,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=39
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,24,949,2307
 
diff --git a/dlls/d3dcompiler_40/Makefile.in b/dlls/d3dcompiler_40/Makefile.in
index 8c7702f3e112..30955de9de0e 100644
--- a/dlls/d3dcompiler_40/Makefile.in
+++ b/dlls/d3dcompiler_40/Makefile.in
@@ -4,8 +4,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=40
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,24,950,2656
 
diff --git a/dlls/d3dcompiler_41/Makefile.in b/dlls/d3dcompiler_41/Makefile.in
index cf80961b82f2..018cebb6998b 100644
--- a/dlls/d3dcompiler_41/Makefile.in
+++ b/dlls/d3dcompiler_41/Makefile.in
@@ -4,8 +4,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=41
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,26,952,2844
 
diff --git a/dlls/d3dcompiler_42/Makefile.in b/dlls/d3dcompiler_42/Makefile.in
index a6c7ef5e493a..57488b1250da 100644
--- a/dlls/d3dcompiler_42/Makefile.in
+++ b/dlls/d3dcompiler_42/Makefile.in
@@ -5,8 +5,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=42
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,27,952,3022
 
diff --git a/dlls/d3dcompiler_43/Makefile.in b/dlls/d3dcompiler_43/Makefile.in
index 2b9e53fd3cef..9b33433fa260 100644
--- a/dlls/d3dcompiler_43/Makefile.in
+++ b/dlls/d3dcompiler_43/Makefile.in
@@ -4,8 +4,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=43
 IMPORTS   = wined3d
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,29,952,3111
 
diff --git a/dlls/d3dcompiler_46/Makefile.in b/dlls/d3dcompiler_46/Makefile.in
index 0c6a71ae0140..0955c2ec8814 100644
--- a/dlls/d3dcompiler_46/Makefile.in
+++ b/dlls/d3dcompiler_46/Makefile.in
@@ -5,8 +5,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=46
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 9,30,9200,20789
 
diff --git a/dlls/d3dcompiler_47/Makefile.in b/dlls/d3dcompiler_47/Makefile.in
index f247b5bea486..4891ec909ff1 100644
--- a/dlls/d3dcompiler_47/Makefile.in
+++ b/dlls/d3dcompiler_47/Makefile.in
@@ -5,8 +5,6 @@ EXTRADEFS = -DD3D_COMPILER_VERSION=47
 PARENTSRC = ../d3dcompiler_43
 EXTRAINCL = $(VKD3D_PE_CFLAGS)
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DCompiler"
 VER_PRODUCTVERSION      = 6,3,9600,16984
 
diff --git a/dlls/d3dim/Makefile.in b/dlls/d3dim/Makefile.in
index e776208c2344..8c1d1cd6f80f 100644
--- a/dlls/d3dim/Makefile.in
+++ b/dlls/d3dim/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = d3dim.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine Direct3D Immediate Mode Utility Functions"
 VER_PRODUCTVERSION      = 5,3,2180,14
diff --git a/dlls/d3dim700/Makefile.in b/dlls/d3dim700/Makefile.in
index badf5bb1736e..3bfba33ededb 100644
--- a/dlls/d3dim700/Makefile.in
+++ b/dlls/d3dim700/Makefile.in
@@ -1,5 +1,3 @@
 MODULE    = d3dim700.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_PRODUCTVERSION = 5,3,2600,5512
diff --git a/dlls/d3dx10_33/Makefile.in b/dlls/d3dx10_33/Makefile.in
index fded1194421e..f02dce6f7b2d 100644
--- a/dlls/d3dx10_33/Makefile.in
+++ b/dlls/d3dx10_33/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = d3dx10_33.dll
 IMPORTLIB = d3dx10_33
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX10"
 VER_PRODUCTVERSION      = 9,18,904,21
 
diff --git a/dlls/d3dx10_34/Makefile.in b/dlls/d3dx10_34/Makefile.in
index 447151645392..455d640856da 100644
--- a/dlls/d3dx10_34/Makefile.in
+++ b/dlls/d3dx10_34/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = d3dx10_34.dll
 IMPORTLIB = d3dx10_34
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX10"
 VER_PRODUCTVERSION      = 9,19,949,46
 
diff --git a/dlls/d3dx10_35/Makefile.in b/dlls/d3dx10_35/Makefile.in
index 83cff7b6a847..10227d7f6864 100644
--- a/dlls/d3dx10_35/Makefile.in
+++ b/dlls/d3dx10_35/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = d3dx10_35.dll
 IMPORTLIB = d3dx10_35
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX10"
 VER_PRODUCTVERSION      = 9,19,949,1104
 
diff --git a/dlls/d3dx10_36/Makefile.in b/dlls/d3dx10_36/Makefile.in
index f77a1793f475..964932dd8131 100644
--- a/dlls/d3dx10_36/Makefile.in
+++ b/dlls/d3dx10_36/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = d3dx10_36.dll
 IMPORTLIB = d3dx10_36
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX10"
 VER_PRODUCTVERSION      = 9,19,949,2009
 
diff --git a/dlls/d3dx10_37/Makefile.in b/dlls/d3dx10_37/Makefile.in
index bb5a47dcd95e..1c7a4c8e1c0f 100644
--- a/dlls/d3dx10_37/Makefile.in
+++ b/dlls/d3dx10_37/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = d3dx10_37.dll
 IMPORTLIB = d3dx10_37
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX10"
 VER_PRODUCTVERSION      = 9,19,949,2187
 
diff --git a/dlls/d3dx10_38/Makefile.in b/dlls/d3dx10_38/Makefile.in
index 43fb7a087a11..3cf97808fdc5 100644
--- a/dlls/d3dx10_38/Makefile.in
+++ b/dlls/d3dx10_38/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = d3dx10_38.dll
 IMPORTLIB = d3dx10_38
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX10"
 VER_PRODUCTVERSION      = 9,23,949,2378
 
diff --git a/dlls/d3dx10_39/Makefile.in b/dlls/d3dx10_39/Makefile.in
index f795bc0e3848..2f83f5e21349 100644
--- a/dlls/d3dx10_39/Makefile.in
+++ b/dlls/d3dx10_39/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = d3dx10_39.dll
 IMPORTLIB = d3dx10_39
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX10"
 VER_PRODUCTVERSION      = 9,24,949,2307
 
diff --git a/dlls/d3dx10_40/Makefile.in b/dlls/d3dx10_40/Makefile.in
index f287a86b9a25..4d4c7b1b5dcf 100644
--- a/dlls/d3dx10_40/Makefile.in
+++ b/dlls/d3dx10_40/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = d3dx10_40.dll
 IMPORTLIB = d3dx10_40
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX10"
 VER_PRODUCTVERSION      = 9,24,950,2656
 
diff --git a/dlls/d3dx10_41/Makefile.in b/dlls/d3dx10_41/Makefile.in
index 6f3f26508836..4d91f34ede1a 100644
--- a/dlls/d3dx10_41/Makefile.in
+++ b/dlls/d3dx10_41/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = d3dx10_41.dll
 IMPORTLIB = d3dx10_41
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX10"
 VER_PRODUCTVERSION      = 9,26,952,2844
 
diff --git a/dlls/d3dx10_42/Makefile.in b/dlls/d3dx10_42/Makefile.in
index 57116d63b30f..22aa2a3ebd11 100644
--- a/dlls/d3dx10_42/Makefile.in
+++ b/dlls/d3dx10_42/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = d3dx10_42.dll
 IMPORTLIB = d3dx10_42
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX10"
 VER_PRODUCTVERSION      = 9,29,952,3001
 
diff --git a/dlls/d3dx10_43/Makefile.in b/dlls/d3dx10_43/Makefile.in
index 6e593686d55c..751087490c0c 100644
--- a/dlls/d3dx10_43/Makefile.in
+++ b/dlls/d3dx10_43/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d10_1 d3dcompiler dxguid uuid gdi32 ole32 user32
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX10"
 VER_PRODUCTVERSION      = 9,29,952,3111
 
diff --git a/dlls/d3dx10_43/d3dx10_43_main.c b/dlls/d3dx10_43/d3dx10_43_main.c
index ec7407508d30..ba6de566576c 100644
--- a/dlls/d3dx10_43/d3dx10_43_main.c
+++ b/dlls/d3dx10_43/d3dx10_43_main.c
@@ -168,11 +168,3 @@ D3DX_CPU_OPTIMIZATION WINAPI D3DXCpuOptimizations(BOOL enable)
 
     return D3DX_NOT_OPTIMIZED;
 }
-
-HRESULT WINAPI D3DX10LoadTextureFromTexture(ID3D10Resource *src_texture, D3DX10_TEXTURE_LOAD_INFO *load_info,
-        ID3D10Resource *dst_texture)
-{
-    FIXME("src_texture %p, load_info %p, dst_texture %p stub!\n", src_texture, load_info, dst_texture);
-
-    return E_NOTIMPL;
-}
diff --git a/dlls/d3dx10_43/tests/d3dx10.c b/dlls/d3dx10_43/tests/d3dx10.c
index 252a64cddd23..0008a995ff8b 100644
--- a/dlls/d3dx10_43/tests/d3dx10.c
+++ b/dlls/d3dx10_43/tests/d3dx10.c
@@ -843,6 +843,114 @@ static const uint8_t dds_dxt10_4_4[] =
     0xf0,0xf0,0xf0,0xf0,
 };
 
+/* A 4x4 2D texture array with 2 elements and 2 mips. */
+static const uint8_t dds_2d_array_4_4[] =
+{
+    0x44,0x44,0x53,0x20,0x7c,0x00,0x00,0x00,0x01,0x10,0x00,0x00,0x04,0x00,0x00,0x00,
+    0x04,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,
+    0x04,0x00,0x00,0x00,0x44,0x58,0x31,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
+    0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,
+    0x00,0xff,0xff,0xff,
+};
+
+static const uint8_t dds_2d_array_4_4_data[] =
+{
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
+    0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,
+};
+
+/* A 4x4x4 3D texture with 2 mips. */
+static const uint8_t dds_dxt10_3d_texture_4_4[] =
+{
+    0x44,0x44,0x53,0x20,0x7c,0x00,0x00,0x00,0x01,0x10,0x80,0x00,0x04,0x00,0x00,0x00,
+    0x04,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,
+    0x04,0x00,0x00,0x00,0x44,0x58,0x31,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x1c,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
+    0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
+    0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
+    0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
+    0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,
+    0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,
+    0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,
+    0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,
+    0x00,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,
+    0xff,0x00,0x80,0x80,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0x80,0x80,0x80,0xff,
+};
+
+/* 8x8 DXT10 DDS file with a format of DXGI_FORMAT_R8G8B8A8_UNORM and 4 mips. */
+static const uint8_t dds_dxt10_8_8[] =
+{
+    0x44,0x44,0x53,0x20,0x7c,0x00,0x00,0x00,0x01,0x10,0x00,0x00,0x08,0x00,0x00,0x00,
+    0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,
+    0x04,0x00,0x00,0x00,0x44,0x58,0x31,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+    0x80,0x80,0x80,0xff,0x40,0x40,0x40,0xff,0x40,0x40,0x40,0xff,0x40,0x40,0x40,0xff,
+    0x40,0x40,0x40,0xff,0x40,0x40,0x40,0xff,0x40,0x40,0x40,0xff,0x40,0x40,0x40,0xff,
+    0x40,0x40,0x40,0xff,0x40,0x40,0x40,0xff,0x40,0x40,0x40,0xff,0x40,0x40,0x40,0xff,
+    0x40,0x40,0x40,0xff,0x40,0x40,0x40,0xff,0x40,0x40,0x40,0xff,0x40,0x40,0x40,0xff,
+    0x40,0x40,0x40,0xff,0x20,0x20,0x20,0xff,0x20,0x20,0x20,0xff,0x20,0x20,0x20,0xff,
+    0x20,0x20,0x20,0xff,0x10,0x10,0x10,0xff,
+};
+
 /* 1x1 wmp image */
 static const uint8_t test_wmp[] =
 {
@@ -1701,6 +1809,7 @@ test_image[] =
         test_bmp_24bpp,      sizeof(test_bmp_24bpp),         test_bmp_24bpp_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_BMP}
     },
+    /* 5. */
     {
         test_bmp_32bpp_xrgb, sizeof(test_bmp_32bpp_xrgb),    test_bmp_32bpp_xrgb_data,
         {2, 2, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_BMP}
@@ -1721,6 +1830,7 @@ test_image[] =
         test_gif,            sizeof(test_gif),               test_gif_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_GIF}
     },
+    /* 10. */
     {
         test_tiff,           sizeof(test_tiff),              test_tiff_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_TIFF}
@@ -1741,6 +1851,7 @@ test_image[] =
         test_dds_24bpp,      sizeof(test_dds_24bpp),         test_dds_24bpp_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_DDS}
     },
+    /* 15. */
     {
         test_dds_32bpp,      sizeof(test_dds_32bpp),         test_dds_32bpp_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_DDS}
@@ -1761,6 +1872,7 @@ test_image[] =
         test_dds_dxt1,       sizeof(test_dds_dxt1),          test_dds_dxt1_data,
         {4, 4, 1, 1, 1, 0,   DXGI_FORMAT_BC1_UNORM,          D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_DDS}
     },
+    /* 20. */
     {
         test_dds_dxt1_4x8,   sizeof(test_dds_dxt1_4x8),      test_dds_dxt1_4x8_data,
         {4, 8, 1, 1, 4, 0,   DXGI_FORMAT_BC1_UNORM,          D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_DDS}
@@ -1781,6 +1893,7 @@ test_image[] =
         test_dds_dxt5,       sizeof(test_dds_dxt5),          test_dds_dxt5_data,
         {4, 2, 1, 1, 1, 0,   DXGI_FORMAT_BC3_UNORM,          D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_DDS}
     },
+    /* 25. */
     {
         test_dds_dxt5_8x8,   sizeof(test_dds_dxt5_8x8),      test_dds_dxt5_8x8_data,
         {8, 8, 1, 1, 4, 0,   DXGI_FORMAT_BC3_UNORM,          D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_DDS}
@@ -1801,6 +1914,11 @@ test_image[] =
         test_dds_volume,     sizeof(test_dds_volume),        test_dds_volume_data,
         {4, 4, 2, 1, 3, 0,   DXGI_FORMAT_BC2_UNORM,          D3D10_RESOURCE_DIMENSION_TEXTURE3D, D3DX10_IFF_DDS}
     },
+    /* 30. */
+    {
+        dds_2d_array_4_4,    sizeof(dds_2d_array_4_4),       dds_2d_array_4_4_data,
+        {4, 4, 1, 2, 2, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_DDS}
+    },
     {
         test_wmp,            sizeof(test_wmp),               test_wmp_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D10_RESOURCE_DIMENSION_TEXTURE2D, D3DX10_IFF_WMP}
@@ -3053,6 +3171,73 @@ static void check_resource_data(ID3D10Resource *resource, const struct test_imag
     }
 }
 
+#define check_test_resource_data(resource, data) check_test_resource_data_(__LINE__, resource, data)
+static void check_test_resource_data_(unsigned int line, ID3D10Resource *resource, const void *expected_data)
+{
+    D3D10_RESOURCE_DIMENSION resource_dimension;
+    const uint8_t *cur_data = expected_data;
+    uint32_t levels, layers, i, j, fmt_bpp;
+    struct resource_readback rb = { 0 };
+    DXGI_FORMAT format;
+    HRESULT hr;
+
+    ID3D10Resource_GetType(resource, &resource_dimension);
+    switch (resource_dimension)
+    {
+        case D3D10_RESOURCE_DIMENSION_TEXTURE2D:
+        {
+            D3D10_TEXTURE2D_DESC desc_2d;
+            ID3D10Texture2D *tex_2d;
+
+            hr = ID3D10Resource_QueryInterface(resource, &IID_ID3D10Texture2D, (void **)&tex_2d);
+            ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n",  hr);
+            ID3D10Texture2D_GetDesc(tex_2d, &desc_2d);
+            ID3D10Texture2D_Release(tex_2d);
+
+            levels = desc_2d.MipLevels;
+            layers = desc_2d.ArraySize;
+            format = desc_2d.Format;
+            break;
+        }
+
+        case D3D10_RESOURCE_DIMENSION_TEXTURE3D:
+        {
+            D3D10_TEXTURE3D_DESC desc_3d;
+            ID3D10Texture3D *tex_3d;
+
+            hr = ID3D10Resource_QueryInterface(resource, &IID_ID3D10Texture3D, (void **)&tex_3d);
+            ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n",  hr);
+            ID3D10Texture3D_GetDesc(tex_3d, &desc_3d);
+            ID3D10Texture3D_Release(tex_3d);
+
+            levels = desc_3d.MipLevels;
+            layers = 1;
+            format = desc_3d.Format;
+            break;
+        }
+
+        default:
+            assert(0 && "Unsupported resource type.");
+            break;
+    }
+
+    fmt_bpp = (get_bpp_from_format(format) + 7) / 8;
+    for (i = 0; i < layers; ++i)
+    {
+        for (j = 0; j < levels; ++j)
+        {
+            winetest_push_context("Layer %u, level %u", i, j);
+            get_resource_readback(resource, (i * levels) + j, &rb);
+
+            _check_test_readback(__FILE__, line, &rb, cur_data, rb.width, rb.height, rb.depth, format, 0);
+            cur_data += (rb.width * fmt_bpp * rb.height * rb.depth);
+
+            release_resource_readback(&rb);
+            winetest_pop_context();
+        }
+    }
+}
+
 static void test_D3DX10UnsetAllDeviceObjects(void)
 {
     static const D3D10_INPUT_ELEMENT_DESC layout_desc[] =
@@ -5960,6 +6145,560 @@ static void test_srgb_filter_flags(void)
     ok(!ID3D10Device_Release(device), "Unexpected refcount.\n");
 }
 
+struct test_resource
+{
+    D3D10_RESOURCE_DIMENSION type;
+    union
+    {
+        D3D10_TEXTURE2D_DESC desc_2d;
+        D3D10_TEXTURE3D_DESC desc_3d;
+    } desc;
+};
+
+static void init_subresource_data(D3D10_SUBRESOURCE_DATA *subresources, const void *data, uint32_t width, uint32_t height,
+        uint32_t depth, uint32_t mip_levels, uint32_t array_size, DXGI_FORMAT format)
+{
+    const uint8_t *pixel_ptr = data;
+    uint32_t i, j;
+
+    for (i = 0; i < array_size; ++i)
+    {
+        uint32_t tmp_width, tmp_height, tmp_depth;
+
+        tmp_width = width;
+        tmp_height = height;
+        tmp_depth = depth;
+        for (j = 0; j < mip_levels; ++j)
+        {
+            D3D10_SUBRESOURCE_DATA *subresource = &subresources[(i * mip_levels) + j];
+
+            subresource->pSysMem = pixel_ptr;
+            subresource->SysMemPitch = (tmp_width * get_bpp_from_format(format) + 7) / 8;
+            subresource->SysMemSlicePitch = subresource->SysMemPitch * tmp_height;
+            if (is_block_compressed(format))
+                subresource->SysMemPitch *= 4;
+            pixel_ptr += (subresource->SysMemSlicePitch * tmp_depth);
+
+            tmp_width  = max(tmp_width  / 2, 1);
+            tmp_height = max(tmp_height / 2, 1);
+            tmp_depth  = max(tmp_depth  / 2, 1);
+        }
+    }
+}
+
+static ID3D10Resource *create_texture_resource(ID3D10Device *device, const struct test_resource *test_rsrc)
+{
+    D3D10_SUBRESOURCE_DATA subresources[16] = { 0 };
+    uint8_t buffer[8192] = { 0 };
+    ID3D10Resource *ret = NULL;
+    HRESULT hr = S_OK;
+
+    switch (test_rsrc->type)
+    {
+        case D3D10_RESOURCE_DIMENSION_TEXTURE2D:
+        {
+            const D3D10_TEXTURE2D_DESC *desc_2d = &test_rsrc->desc.desc_2d;
+
+            assert((desc_2d->MipLevels * desc_2d->ArraySize) <= ARRAY_SIZE(subresources));
+            init_subresource_data(subresources, buffer, desc_2d->Width, desc_2d->Height, 1, desc_2d->MipLevels,
+                    desc_2d->ArraySize, desc_2d->Format);
+            hr = ID3D10Device_CreateTexture2D(device, desc_2d, subresources, (ID3D10Texture2D **)&ret);
+            break;
+        }
+
+        case D3D10_RESOURCE_DIMENSION_TEXTURE3D:
+        {
+            const D3D10_TEXTURE3D_DESC *desc_3d = &test_rsrc->desc.desc_3d;
+
+            assert(desc_3d->MipLevels <= ARRAY_SIZE(subresources));
+            init_subresource_data(subresources, buffer, desc_3d->Width, desc_3d->Height, desc_3d->Depth,
+                    desc_3d->MipLevels, 1, desc_3d->Format);
+            hr = ID3D10Device_CreateTexture3D(device, desc_3d, subresources, (ID3D10Texture3D **)&ret);
+            break;
+        }
+
+        default:
+            assert(0 && "Invalid resource type.");
+    }
+
+    if (FAILED(hr))
+        trace("Failed to create texture, hr %#lx.\n", hr);
+    return ret;
+}
+
+static void test_load_texture_from_texture(void)
+{
+    static const uint8_t test_10_expected[] =
+    {
+        0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+        0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+        0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+        0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+        0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
+        0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+        0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+        0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+        0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+        0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
+    };
+    static const uint8_t test_15_expected[] =
+    {
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,0x80,0x80,0x80,0xff,
+        0x80,0x80,0x80,0xff,
+    };
+    static const uint8_t test_16_expected[] =
+    {
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0xff,
+        0x10,0x10,0x10,0xff,
+    };
+    static const uint8_t test_18_expected[] =
+    {
+        0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    };
+    static const D3DX10_IMAGE_LOAD_INFO d3dx10_from_file_load_info =
+    {
+        D3DX10_FROM_FILE, D3DX10_FROM_FILE, D3DX10_FROM_FILE, D3DX10_DEFAULT,        D3DX10_FROM_FILE, D3DX10_DEFAULT,
+        D3DX10_DEFAULT,   D3DX10_DEFAULT,   D3DX10_DEFAULT,   DXGI_FORMAT_FROM_FILE, D3DX10_FILTER_NONE,
+        D3DX10_FILTER_NONE, NULL
+    };
+    static const struct
+    {
+        D3DX10_TEXTURE_LOAD_INFO load_info;
+        const uint8_t *src_data;
+        unsigned int src_data_size;
+        D3D10_BOX src_box;
+        BOOL use_src_box;
+
+        struct test_resource dst_rsrc;
+        D3D10_BOX dst_box;
+        BOOL use_dst_box;
+        HRESULT expected_hr;
+        const uint8_t *expected_dst;
+        unsigned int broken_below_version;
+        BOOL todo_hr;
+    } tests[] =
+    {
+        /*
+         * Source and destination are the same texture. If the elements and
+         * levels match, the function will fail.
+         */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_DEFAULT, D3DX10_DEFAULT },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            { D3D10_RESOURCE_DIMENSION_UNKNOWN }, { 0 }, FALSE, D3DERR_INVALIDCALL
+        },
+        /* Invalid pSrcBox dimensions. */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_DEFAULT, D3DX10_DEFAULT },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 1, 0, 0, 0, 0, 0 }, TRUE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 2, 2, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, D3DERR_INVALIDCALL, .broken_below_version = 40
+        },
+        /* Invalid pDstBox dimensions. */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_DEFAULT, D3DX10_DEFAULT },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 2, 2, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0, 1, 0, 0, 0, 0 }, TRUE, D3DERR_INVALIDCALL, .broken_below_version = 40
+        },
+        /*
+         * Invalid filter flags. Still validated even if SrcFirstMip is higher
+         * than number of levels in the source texture.
+         */
+        {
+            { NULL, NULL, 3, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, 9, D3DX10_DEFAULT },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, D3DERR_INVALIDCALL
+        },
+        /*
+         * Filter flags aren't validated if DstFirstMip is higher than the
+         * number of levels.
+         */
+        {
+            { NULL, NULL, 0, 1, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, 9, D3DX10_FILTER_NONE },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, S_OK
+        },
+        /*
+         * 5.
+         * Filter flags aren't validated if SrcFirstElement is higher than the
+         * number of source elements.
+         */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 2, 0, D3DX10_DEFAULT, 9, D3DX10_FILTER_NONE },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, S_OK
+        },
+        /*
+         * Filter flags aren't validated if DstFirstElement is higher than the
+         * number of destination elements.
+         */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 1, D3DX10_DEFAULT, 9, D3DX10_FILTER_NONE },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, S_OK
+        },
+        /* Invalid mip filter flags. */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_DEFAULT, 9 },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 3, 2, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, D3DERR_INVALIDCALL
+        },
+        /*
+         * Invalid mip filter flags. If no mips are generated, no validation is
+         * done.
+         */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_FILTER_NONE, 9 },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 2, 2, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, S_OK, &dds_2d_array_4_4[0x94]
+        },
+        /* Destination texture is immutable. */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_DEFAULT, D3DX10_DEFAULT },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 2, 2, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_IMMUTABLE, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, D3DERR_INVALIDCALL
+        },
+        /*
+         * 10.
+         * Source and destination are the same texture.
+         * Source is level 0 of element 0.
+         * Destination is level 0 of element 1.
+         * Load levels 0-1 from element 0 into levels 0-1 of element 1.
+         */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 1, D3DX10_DEFAULT, D3DX10_FILTER_NONE, D3DX10_DEFAULT },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            { D3D10_RESOURCE_DIMENSION_UNKNOWN }, { 0 }, FALSE, S_OK, test_10_expected
+        },
+        /* Load a 3D texture into a 2D texture. */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_FILTER_NONE, D3DX10_DEFAULT },
+            dds_dxt10_3d_texture_4_4, sizeof(dds_dxt10_3d_texture_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, S_OK, &dds_dxt10_3d_texture_4_4[0x94]
+        },
+        /* Load a 3D texture into a 2D texture with a source box offset. */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_FILTER_NONE, D3DX10_DEFAULT },
+            dds_dxt10_3d_texture_4_4, sizeof(dds_dxt10_3d_texture_4_4), { 0, 0, 1, 4, 4, 2 }, TRUE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, S_OK, &dds_dxt10_3d_texture_4_4[0xd4]
+        },
+        /* Load a 3D texture into a 3D texture with identical dimensions. */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_FILTER_NONE, D3DX10_DEFAULT },
+            dds_dxt10_3d_texture_4_4, sizeof(dds_dxt10_3d_texture_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE3D,
+                .desc.desc_3d =
+                {
+                    4, 4, 4, 2, DXGI_FORMAT_R8G8B8A8_UNORM, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, S_OK, &dds_dxt10_3d_texture_4_4[0x94]
+        },
+        /* Load a 3D texture into a 3D texture with a first mip offset. */
+        {
+            { NULL, NULL, 1, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_FILTER_NONE, D3DX10_DEFAULT },
+            dds_dxt10_3d_texture_4_4, sizeof(dds_dxt10_3d_texture_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE3D,
+                .desc.desc_3d =
+                {
+                    2, 2, 2, 1, DXGI_FORMAT_R8G8B8A8_UNORM, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, S_OK, &dds_dxt10_3d_texture_4_4[0x194]
+        },
+        /*
+         * 15.
+         * Source and destination are the same texture.
+         * Source is level 0 of element 0.
+         * Destination is level 1 of element 0.
+         * Load level n into level n+1.
+         */
+        {
+            { NULL, NULL, 0, 1, 3, 0, 0, D3DX10_DEFAULT, D3DX10_FILTER_NONE, 9 },
+            dds_dxt10_8_8, sizeof(dds_dxt10_8_8), { 0 }, FALSE,
+            { D3D10_RESOURCE_DIMENSION_UNKNOWN }, { 0 }, FALSE, S_OK, test_15_expected
+        },
+        /*
+         * Source and destination are the same texture.
+         * Source is level 3 of element 0.
+         * Destination is level 0 of element 0.
+         * Load level 3 into level 0, generate levels 1-3 from level 0.
+         */
+        {
+            { NULL, NULL, 3, 0, 4, 0, 0, D3DX10_DEFAULT, D3DX10_FILTER_POINT, D3DX10_FILTER_POINT },
+            dds_dxt10_8_8, sizeof(dds_dxt10_8_8), { 0 }, FALSE,
+            { D3D10_RESOURCE_DIMENSION_UNKNOWN }, { 0 }, FALSE, S_OK, test_16_expected
+        },
+        /*
+         * Argument of 0 for NumElements/NumMips matches behavior of
+         * D3DX10_DEFAULT.
+         */
+        {
+            { NULL, NULL, 0, 0, 0, 0, 0, 0, D3DX10_FILTER_NONE, 9 },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 2, 2, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, S_OK, &dds_2d_array_4_4[0x94]
+        },
+        /*
+         * Empty source box results in a 1x1x1 box.
+         */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_FILTER_NONE, 9 },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, TRUE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, FALSE, S_OK, test_18_expected
+        },
+        /*
+         * Empty destination box results in a 1x1x1 box.
+         */
+        {
+            { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT, D3DX10_FILTER_NONE, 9 },
+            dds_2d_array_4_4, sizeof(dds_2d_array_4_4), { 0 }, FALSE,
+            {
+                D3D10_RESOURCE_DIMENSION_TEXTURE2D,
+                .desc.desc_2d =
+                {
+                    4, 4, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_SHADER_RESOURCE,
+                    0, 0,
+                }
+            }, { 0 }, TRUE, S_OK, test_18_expected
+        },
+    };
+    const uint8_t empty_buffer[8192] = { 0 };
+    ID3D10Resource *src_rsrc, *dst_rsrc;
+    const uint8_t *expected_dst;
+    ID3D10Device *device;
+    unsigned int i;
+    HRESULT hr;
+
+    if (D3DX10_SDK_VERSION <= 34)
+    {
+        skip("D3DX10LoadTextureFromTexture() behavior is broken in version 34 and below.\n");
+        return;
+    }
+
+    device = create_device();
+    if (!device)
+    {
+        skip("Failed to create device, skipping tests.\n");
+        return;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(tests); ++i)
+    {
+        D3DX10_IMAGE_LOAD_INFO img_load_info = d3dx10_from_file_load_info;
+        D3DX10_TEXTURE_LOAD_INFO load_info = tests[i].load_info;
+        D3D10_BOX src_box = tests[i].src_box;
+        D3D10_BOX dst_box = tests[i].dst_box;
+
+        if (D3DX10_SDK_VERSION < tests[i].broken_below_version)
+        {
+            skip("Test %u broken on version %u, skipping test.\n", i, D3DX10_SDK_VERSION);
+            continue;
+        }
+
+        winetest_push_context("Test %u", i);
+
+        hr = D3DX10CreateTextureFromMemory(device, tests[i].src_data, tests[i].src_data_size, &img_load_info, NULL,
+                &src_rsrc, NULL);
+        ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+
+        load_info.pSrcBox = (tests[i].use_src_box) ? &src_box : NULL;
+        load_info.pDstBox = (tests[i].use_dst_box) ? &dst_box : NULL;
+        expected_dst = (tests[i].expected_dst) ? tests[i].expected_dst : empty_buffer;
+
+        dst_rsrc = (tests[i].dst_rsrc.type) ? create_texture_resource(device, &tests[i].dst_rsrc) : src_rsrc;
+        ok(!!dst_rsrc, "Got unexpected dst_rsrc %p.\n", dst_rsrc);
+
+        hr = D3DX10LoadTextureFromTexture(src_rsrc, &load_info, dst_rsrc);
+        todo_wine_if(tests[i].todo_hr) ok(hr == tests[i].expected_hr, "Unexpected hr %#lx.\n", hr);
+        if (SUCCEEDED(hr))
+            check_test_resource_data(dst_rsrc, expected_dst);
+
+        ID3D10Resource_Release(src_rsrc);
+        if (dst_rsrc != src_rsrc)
+            ID3D10Resource_Release(dst_rsrc);
+
+        winetest_pop_context();
+    }
+
+    /* NULL load_info argument, gets default load_info values. */
+    hr = D3DX10CreateTextureFromMemory(device, dds_2d_array_4_4, sizeof(dds_2d_array_4_4),
+            (D3DX10_IMAGE_LOAD_INFO *)&d3dx10_from_file_load_info, NULL, &src_rsrc, NULL);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+
+    dst_rsrc = create_texture_resource(device, &tests[8].dst_rsrc);
+    ok(!!dst_rsrc, "Got unexpected dst_rsrc %p.\n", dst_rsrc);
+
+    /* Test NULL arguments for source/dest resource. */
+    hr = D3DX10LoadTextureFromTexture(src_rsrc, NULL, NULL);
+    ok(hr == D3DERR_INVALIDCALL, "Unexpected hr %#lx.\n", hr);
+
+    hr = D3DX10LoadTextureFromTexture(NULL, NULL, dst_rsrc);
+    ok(hr == D3DERR_INVALIDCALL, "Unexpected hr %#lx.\n", hr);
+
+    hr = D3DX10LoadTextureFromTexture(NULL, NULL, NULL);
+    ok(hr == D3DERR_INVALIDCALL, "Unexpected hr %#lx.\n", hr);
+
+    /* NULL load_info argument, gets default load_info values. */
+    hr = D3DX10LoadTextureFromTexture(src_rsrc, NULL, dst_rsrc);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_test_resource_data(dst_rsrc, &dds_2d_array_4_4[0x94]);
+
+    ID3D10Resource_Release(src_rsrc);
+    ID3D10Resource_Release(dst_rsrc);
+
+    /* NULL load_info argument on 3D texture. */
+    hr = D3DX10CreateTextureFromMemory(device, dds_dxt10_3d_texture_4_4, sizeof(dds_dxt10_3d_texture_4_4),
+            (D3DX10_IMAGE_LOAD_INFO *)&d3dx10_from_file_load_info, NULL, &src_rsrc, NULL);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+
+    dst_rsrc = create_texture_resource(device, &tests[13].dst_rsrc);
+    ok(!!dst_rsrc, "Got unexpected dst_rsrc %p.\n", dst_rsrc);
+
+    hr = D3DX10LoadTextureFromTexture(src_rsrc, NULL, dst_rsrc);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    check_test_resource_data(dst_rsrc, &dds_dxt10_3d_texture_4_4[0x94]);
+
+    ID3D10Resource_Release(src_rsrc);
+    ID3D10Resource_Release(dst_rsrc);
+
+    ok(!ID3D10Device_Release(device), "Unexpected refcount.\n");
+}
+
 #define check_rect(rect, left, top, right, bottom) _check_rect(__LINE__, rect, left, top, right, bottom)
 static inline void _check_rect(unsigned int line, const RECT *rect, int left, int top, int right, int bottom)
 {
@@ -7451,4 +8190,5 @@ START_TEST(d3dx10)
     test_image_filters();
     test_dxt_formats();
     test_srgb_filter_flags();
+    test_load_texture_from_texture();
 }
diff --git a/dlls/d3dx10_43/texture.c b/dlls/d3dx10_43/texture.c
index bb312ae42ff3..1a54609f6eff 100644
--- a/dlls/d3dx10_43/texture.c
+++ b/dlls/d3dx10_43/texture.c
@@ -27,6 +27,8 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
 
+#define D3DERR_INVALIDCALL  0x8876086c
+
 /*
  * These are mappings from legacy DDS header formats to DXGI formats. Some
  * don't map to a DXGI_FORMAT at all, and some only map to the default format.
@@ -637,7 +639,7 @@ void init_load_info(const D3DX10_IMAGE_LOAD_INFO *load_info, D3DX10_IMAGE_LOAD_I
 HRESULT load_texture_data(const void *data, SIZE_T size, D3DX10_IMAGE_LOAD_INFO *load_info,
         D3D10_SUBRESOURCE_DATA **resource_data)
 {
-    uint32_t loaded_mip_level_count, max_mip_level_count;
+    uint32_t loaded_mip_level_count, max_mip_level_count, loaded_layer_count;
     const struct pixel_format_desc *fmt_desc, *src_desc;
     struct d3dx_subresource_data *sub_rsrcs = NULL;
     D3DX10_IMAGE_INFO img_info;
@@ -661,12 +663,11 @@ HRESULT load_texture_data(const void *data, SIZE_T size, D3DX10_IMAGE_LOAD_INFO
         goto end;
     }
 
-    if ((!(img_info.MiscFlags & D3D10_RESOURCE_MISC_TEXTURECUBE) || img_info.ArraySize != 6)
-            && img_info.ArraySize != 1)
+    loaded_layer_count = img_info.ArraySize;
+    if ((loaded_layer_count > 1) && (img_info.ResourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE3D))
     {
-        FIXME("img_info.ArraySize = %u not supported.\n", img_info.ArraySize);
-        hr = E_NOTIMPL;
-        goto end;
+        TRACE("Ignoring array size variable %u for 3D texture.\n", img_info.ArraySize);
+        loaded_layer_count = 1;
     }
 
     if (load_info->FirstMipLevel == D3DX10_DEFAULT || load_info->FirstMipLevel >= img_info.MipLevels)
@@ -720,13 +721,13 @@ HRESULT load_texture_data(const void *data, SIZE_T size, D3DX10_IMAGE_LOAD_INFO
     }
 
     hr = d3dx_create_subresource_data_for_texture(load_info->Width, load_info->Height, load_info->Depth,
-            load_info->MipLevels, img_info.ArraySize, fmt_desc, &sub_rsrcs);
+            load_info->MipLevels, loaded_layer_count, fmt_desc, &sub_rsrcs);
     if (FAILED(hr))
         goto end;
 
     src_desc = get_d3dx_pixel_format_info(image.format);
     loaded_mip_level_count = min(img_info.MipLevels - load_info->FirstMipLevel, load_info->MipLevels);
-    for (i = 0; i < img_info.ArraySize; ++i)
+    for (i = 0; i < loaded_layer_count; ++i)
     {
         struct volume dst_size = { load_info->Width, load_info->Height, load_info->Depth };
 
@@ -765,7 +766,7 @@ HRESULT load_texture_data(const void *data, SIZE_T size, D3DX10_IMAGE_LOAD_INFO
         }
 
         d3dx_get_mip_level_size(&base_level_size, base_level);
-        for (i = 0; i < img_info.ArraySize; ++i)
+        for (i = 0; i < loaded_layer_count; ++i)
         {
             struct volume src_size, dst_size;
 
@@ -912,3 +913,417 @@ HRESULT WINAPI D3DX10CreateTextureFromMemory(ID3D10Device *device, const void *s
         *hresult = hr;
     return hr;
 }
+
+static void init_d3d10_box(D3D10_BOX *box, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom, uint32_t front,
+        uint32_t back)
+{
+    box->left = left;
+    box->top = top;
+    box->right = max(right, left + 1);
+    box->bottom = max(bottom, top + 1);
+    box->front = front;
+    box->back = max(back, front + 1);
+}
+
+static void d3d10_box_get_level(D3D10_BOX *box, unsigned int level)
+{
+    init_d3d10_box(box, box->left >> level, box->top >> level, box->right >> level, box->bottom >> level,
+            box->front >> level, box->back >> level);
+}
+
+static void d3d10_box_get_next_level(D3D10_BOX *box)
+{
+    init_d3d10_box(box, box->left >> 1, box->top >> 1, box->right >> 1, box->bottom >> 1,
+            box->front >> 1, box->back >> 1);
+}
+
+static BOOL d3d10_box_clamp(D3D10_BOX *box, unsigned int level, struct volume *size)
+{
+    struct volume level_size = *size;
+    BOOL clamped;
+
+    if (level)
+        d3dx_get_mip_level_size(&level_size, level);
+
+    clamped = (box->right > level_size.width) || (box->bottom > level_size.height) || (box->back > level_size.depth);
+    if (box->right > level_size.width)
+    {
+        box->right = level_size.width;
+        box->left = min(box->right - 1, box->left);
+    }
+
+    if (box->bottom > level_size.height)
+    {
+        box->bottom = level_size.height;
+        box->top = min(box->bottom - 1, box->top);
+    }
+
+    if (box->back > level_size.depth)
+    {
+        box->back = level_size.depth;
+        box->front = min(box->back - 1, box->front);
+    }
+
+    return clamped;
+}
+
+static BOOL d3d10_box_is_valid(const D3D10_BOX *box)
+{
+    return (box->left <= box->right) && (box->top <= box->bottom) && (box->front <= box->back);
+}
+
+struct d3dx_texture_info
+{
+    enum d3dx_resource_type resource_type;
+    const struct pixel_format_desc *fmt;
+
+    struct volume size;
+    uint32_t levels;
+    uint32_t layers;
+
+    D3D10_USAGE usage;
+};
+
+static HRESULT d3dx_texture_info_from_d3d10_resource(ID3D10Resource *rsrc, struct d3dx_texture_info *info)
+{
+    D3D10_RESOURCE_DIMENSION rsrc_dim;
+    HRESULT hr;
+
+    ID3D10Resource_GetType(rsrc, &rsrc_dim);
+    switch (rsrc_dim)
+    {
+        case D3D10_RESOURCE_DIMENSION_TEXTURE2D:
+        {
+            D3D10_TEXTURE2D_DESC desc_2d;
+            ID3D10Texture2D *tex_2d;
+
+            hr = ID3D10Resource_QueryInterface(rsrc, &IID_ID3D10Texture2D, (void **)&tex_2d);
+            if (FAILED(hr))
+                return hr;
+            ID3D10Texture2D_GetDesc(tex_2d, &desc_2d);
+            ID3D10Texture2D_Release(tex_2d);
+
+            info->resource_type = D3DX_RESOURCE_TYPE_TEXTURE_2D;
+            info->fmt = get_d3dx_pixel_format_info(d3dx_pixel_format_id_from_dxgi_format(desc_2d.Format));
+            if (is_unknown_format(info->fmt))
+                return E_NOTIMPL;
+            set_volume_struct(&info->size, desc_2d.Width, desc_2d.Height, 1);
+            info->levels = desc_2d.MipLevels;
+            info->layers = desc_2d.ArraySize;
+            info->usage = desc_2d.Usage;
+            break;
+        }
+
+        case D3D10_RESOURCE_DIMENSION_TEXTURE3D:
+        {
+            D3D10_TEXTURE3D_DESC desc_3d;
+            ID3D10Texture3D *tex_3d;
+
+            hr = ID3D10Resource_QueryInterface(rsrc, &IID_ID3D10Texture3D, (void **)&tex_3d);
+            if (FAILED(hr))
+                return hr;
+            ID3D10Texture3D_GetDesc(tex_3d, &desc_3d);
+            ID3D10Texture3D_Release(tex_3d);
+
+            info->resource_type = D3DX_RESOURCE_TYPE_TEXTURE_3D;
+            info->fmt = get_d3dx_pixel_format_info(d3dx_pixel_format_id_from_dxgi_format(desc_3d.Format));
+            if (is_unknown_format(info->fmt))
+                return E_NOTIMPL;
+            set_volume_struct(&info->size, desc_3d.Width, desc_3d.Height, desc_3d.Depth);
+            info->levels = desc_3d.MipLevels;
+            info->layers = 1;
+            info->usage = desc_3d.Usage;
+            break;
+        }
+
+        default:
+            FIXME("Unhandled resource type %d.\n", rsrc_dim);
+            return E_NOTIMPL;
+    }
+
+    return S_OK;
+}
+
+static HRESULT d3d10_staging_resource_from_d3dx_texture_info(ID3D10Device *device, struct d3dx_texture_info *info,
+        BOOL is_dst, ID3D10Resource **rsrc)
+{
+    const UINT cpu_flags = is_dst ? (D3D10_CPU_ACCESS_READ | D3D10_CPU_ACCESS_WRITE) : D3D10_CPU_ACCESS_READ;
+
+    *rsrc = NULL;
+    switch (info->resource_type)
+    {
+        case D3DX_RESOURCE_TYPE_TEXTURE_2D:
+        {
+            D3D10_TEXTURE2D_DESC desc = { info->size.width, info->size.height, 1, 1, 0, { 1, 0 }, D3D10_USAGE_STAGING,
+                                          0, cpu_flags };
+
+            desc.Format = dxgi_format_from_d3dx_pixel_format_id(info->fmt->format);
+            return ID3D10Device_CreateTexture2D(device, &desc, NULL, (ID3D10Texture2D **)rsrc);
+        }
+
+        case D3DX_RESOURCE_TYPE_TEXTURE_3D:
+        {
+            D3D10_TEXTURE3D_DESC desc = { info->size.width, info->size.height, info->size.depth, 1, 0,
+                                          D3D10_USAGE_STAGING, 0, cpu_flags };
+
+            desc.Format = dxgi_format_from_d3dx_pixel_format_id(info->fmt->format);
+            return ID3D10Device_CreateTexture3D(device, &desc, NULL, (ID3D10Texture3D **)rsrc);
+        }
+
+        default:
+            return E_NOTIMPL;
+    }
+}
+
+static HRESULT d3dx_subresource_data_from_d3d10_staging_resource_map(ID3D10Resource *rsrc,
+        enum d3dx_resource_type rtype, BOOL is_dst, struct d3dx_subresource_data *out_data)
+{
+    const D3D10_MAP map_type = is_dst ? D3D10_MAP_READ_WRITE : D3D10_MAP_READ;
+    HRESULT hr;
+
+    memset(out_data, 0, sizeof(*out_data));
+    switch (rtype)
+    {
+        case D3DX_RESOURCE_TYPE_TEXTURE_2D:
+        {
+            ID3D10Texture2D *tex = (ID3D10Texture2D *)rsrc;
+            D3D10_MAPPED_TEXTURE2D map;
+
+            hr = ID3D10Texture2D_Map(tex, 0, map_type, 0, &map);
+            if (SUCCEEDED(hr))
+            {
+                out_data->data = map.pData;
+                out_data->row_pitch = map.RowPitch;
+            }
+            break;
+        }
+
+        case D3DX_RESOURCE_TYPE_TEXTURE_3D:
+        {
+            ID3D10Texture3D *tex = (ID3D10Texture3D *)rsrc;
+            D3D10_MAPPED_TEXTURE3D map;
+
+            hr = ID3D10Texture3D_Map(tex, 0, map_type, 0, &map);
+            if (SUCCEEDED(hr))
+            {
+                out_data->data = map.pData;
+                out_data->row_pitch = map.RowPitch;
+                out_data->slice_pitch = map.DepthPitch;
+            }
+            break;
+        }
+
+        default:
+            hr = E_NOTIMPL;
+            break;
+    }
+
+    return hr;
+}
+
+static void d3d10_staging_resource_unmap(ID3D10Resource *rsrc, enum d3dx_resource_type rtype)
+{
+    switch (rtype)
+    {
+        case D3DX_RESOURCE_TYPE_TEXTURE_2D:
+            ID3D10Texture2D_Unmap((ID3D10Texture2D *)rsrc, 0);
+            break;
+
+        case D3DX_RESOURCE_TYPE_TEXTURE_3D:
+            ID3D10Texture3D_Unmap((ID3D10Texture3D *)rsrc, 0);
+            break;
+
+        default:
+            break;
+    }
+}
+
+static HRESULT d3d10_load_texture_from_texture(struct d3dx_texture_info *src_info, ID3D10Resource *src_rsrc,
+        const D3D10_BOX *src_box, uint32_t src_first_layer, uint32_t src_first_level,
+        struct d3dx_texture_info *dst_info, ID3D10Resource *dst_rsrc, const D3D10_BOX *dst_box,
+        uint32_t dst_first_layer, uint32_t dst_first_level, uint32_t layer_load_count, uint32_t level_load_count,
+        uint32_t filter)
+{
+    ID3D10Resource *src_staging, *dst_staging;
+    ID3D10Device *device = NULL;
+    unsigned int i, j;
+    HRESULT hr;
+
+    if (FAILED(hr = d3dx_validate_filter(filter)))
+    {
+        WARN("Invalid filter argument %#x.\n", filter);
+        return hr;
+    }
+
+    src_staging = dst_staging = NULL;
+    ID3D10Resource_GetDevice(src_rsrc, &device);
+    hr = d3d10_staging_resource_from_d3dx_texture_info(device, src_info, FALSE, &src_staging);
+    if (FAILED(hr))
+        goto exit;
+
+    hr = d3d10_staging_resource_from_d3dx_texture_info(device, dst_info, TRUE, &dst_staging);
+    if (FAILED(hr))
+        goto exit;
+
+    for (i = 0; i < layer_load_count; ++i)
+    {
+        unsigned int src_sub_rsrc_idx = (src_first_layer + i) * src_info->levels + src_first_level;
+        unsigned int dst_sub_rsrc_idx = (dst_first_layer + i) * dst_info->levels + dst_first_level;
+        D3D10_BOX src_level_box = *src_box;
+        D3D10_BOX dst_level_box = *dst_box;
+
+        for (j = 0; j < level_load_count; ++j)
+        {
+            struct d3dx_subresource_data src_sub_rsrc, dst_sub_rsrc;
+            struct d3dx_pixels src_pixels, dst_pixels;
+
+            ID3D10Device_CopySubresourceRegion(device, src_staging, 0, 0, 0, 0, src_rsrc, src_sub_rsrc_idx + j, NULL);
+            hr = d3dx_subresource_data_from_d3d10_staging_resource_map(src_staging, src_info->resource_type, FALSE,
+                    &src_sub_rsrc);
+            if (FAILED(hr))
+                goto exit;
+
+            ID3D10Device_CopySubresourceRegion(device, dst_staging, 0, 0, 0, 0, dst_rsrc, dst_sub_rsrc_idx + j, NULL);
+            hr = d3dx_subresource_data_from_d3d10_staging_resource_map(dst_staging, dst_info->resource_type, TRUE,
+                    &dst_sub_rsrc);
+            if (FAILED(hr))
+                goto exit;
+
+            /* Account for source/destination box offsets. */
+            d3dx_pixels_init(src_sub_rsrc.data, src_sub_rsrc.row_pitch, src_sub_rsrc.slice_pitch, NULL,
+                    src_info->fmt->format, src_level_box.left, src_level_box.top, src_level_box.right,
+                    src_level_box.bottom, src_level_box.front, src_level_box.back, &src_pixels);
+            d3dx_pixels_init(dst_sub_rsrc.data, dst_sub_rsrc.row_pitch, dst_sub_rsrc.slice_pitch, NULL,
+                    dst_info->fmt->format, dst_level_box.left, dst_level_box.top, dst_level_box.right,
+                    dst_level_box.bottom, dst_level_box.front, dst_level_box.back, &dst_pixels);
+
+            hr = d3dx_load_pixels_from_pixels(&dst_pixels, dst_info->fmt, &src_pixels, src_info->fmt, filter, 0);
+            d3d10_staging_resource_unmap(src_staging, src_info->resource_type);
+            d3d10_staging_resource_unmap(dst_staging, dst_info->resource_type);
+            if (FAILED(hr))
+                goto exit;
+
+            ID3D10Device_CopySubresourceRegion(device, dst_rsrc, dst_sub_rsrc_idx + j, 0, 0, 0, dst_staging, 0, &dst_level_box);
+            d3d10_box_get_next_level(&src_level_box);
+            d3d10_box_get_next_level(&dst_level_box);
+        }
+    }
+
+exit:
+    if (device)
+        ID3D10Device_Release(device);
+    if (src_staging)
+        ID3D10Resource_Release(src_staging);
+    if (dst_staging)
+        ID3D10Resource_Release(dst_staging);
+    return hr;
+}
+
+HRESULT WINAPI D3DX10LoadTextureFromTexture(ID3D10Resource *src_texture, D3DX10_TEXTURE_LOAD_INFO *load_info,
+        ID3D10Resource *dst_texture)
+{
+    static const D3DX10_TEXTURE_LOAD_INFO default_load_info = { NULL, NULL, 0, 0, D3DX10_DEFAULT, 0, 0, D3DX10_DEFAULT,
+                                                                D3DX10_DEFAULT, D3DX10_DEFAULT };
+    D3DX10_TEXTURE_LOAD_INFO info = (load_info) ? *load_info : default_load_info;
+    struct d3dx_texture_info src_info, dst_info;
+    uint32_t loaded_level_count;
+    D3D10_BOX src_box, dst_box;
+    HRESULT hr;
+
+    TRACE("src_texture %p, load_info %p, dst_texture %p.\n", src_texture, load_info, dst_texture);
+
+    if (!src_texture || !dst_texture)
+        return D3DERR_INVALIDCALL;
+
+    if ((info.pSrcBox && !d3d10_box_is_valid(info.pSrcBox)) || (info.pDstBox && !d3d10_box_is_valid(info.pDstBox)))
+        return D3DERR_INVALIDCALL;
+
+    /*
+     * If the source and destination texture are the same, we can't load into
+     * the same subresource.
+     */
+    if (src_texture == dst_texture && info.SrcFirstMip == info.DstFirstMip
+            && info.SrcFirstElement == info.DstFirstElement)
+        return D3DERR_INVALIDCALL;
+
+    hr = d3dx_texture_info_from_d3d10_resource(src_texture, &src_info);
+    if (FAILED(hr))
+        return hr;
+
+    hr = d3dx_texture_info_from_d3d10_resource(dst_texture, &dst_info);
+    if (FAILED(hr))
+        return hr;
+
+    /* Destination cannot be immutable. */
+    if (dst_info.usage == D3D10_USAGE_IMMUTABLE)
+        return D3DERR_INVALIDCALL;
+
+    /*
+     * Attempting to load beyond the number of levels/layers in the passed in
+     * textures, nothing to load, return early.
+     */
+    if ((info.DstFirstMip >= dst_info.levels) || (info.SrcFirstElement >= src_info.layers)
+            || (info.DstFirstElement >= dst_info.layers))
+        return S_OK;
+
+    /*
+     * Native doesn't validate the SrcFirstMip argument, and will read OOB if
+     * passed a value higher than the actual number of levels.
+     */
+    if (info.SrcFirstMip >= src_info.levels)
+    {
+        WARN("Attempted to load first mip from source beyond total number of mips, clamping.\n");
+        info.SrcFirstMip = src_info.levels - 1;
+    }
+
+    if (!info.NumElements)
+        info.NumElements = D3DX10_DEFAULT;
+    info.NumElements = min(info.NumElements, min(src_info.layers - info.SrcFirstElement,
+                dst_info.layers - info.DstFirstElement));
+
+    if (!info.NumMips)
+        info.NumMips = D3DX10_DEFAULT;
+    info.NumMips = min(dst_info.levels - info.DstFirstMip, info.NumMips);
+    loaded_level_count = min(src_info.levels - info.SrcFirstMip, info.NumMips);
+
+    if (info.pSrcBox)
+        init_d3d10_box(&src_box, info.pSrcBox->left, info.pSrcBox->top, info.pSrcBox->right, info.pSrcBox->bottom,
+                info.pSrcBox->front, info.pSrcBox->back);
+    else
+        init_d3d10_box(&src_box, 0, 0, src_info.size.width, src_info.size.height, 0, src_info.size.depth);
+    /* Native will do an OOB access in this case, we'll just clamp instead. */
+    if (d3d10_box_clamp(&src_box, info.SrcFirstMip, &src_info.size) && info.pSrcBox)
+        WARN("Clamped passed in pSrcBox values.\n");
+
+    if (info.pDstBox)
+        init_d3d10_box(&dst_box, info.pDstBox->left, info.pDstBox->top, info.pDstBox->right, info.pDstBox->bottom,
+                info.pDstBox->front, info.pDstBox->back);
+    else
+        init_d3d10_box(&dst_box, 0, 0, dst_info.size.width, dst_info.size.height, 0, dst_info.size.depth);
+    /* Native will do an OOB access in this case, we'll just clamp instead. */
+    if (d3d10_box_clamp(&dst_box, info.DstFirstMip, &dst_info.size) && info.pDstBox)
+        WARN("Clamped passed in pDstBox values.\n");
+
+    if (!info.Filter || info.Filter == D3DX10_DEFAULT)
+        info.Filter = D3DX10_FILTER_LINEAR;
+
+    hr = d3d10_load_texture_from_texture(&src_info, src_texture, &src_box, info.SrcFirstElement, info.SrcFirstMip,
+            &dst_info, dst_texture, &dst_box, info.DstFirstElement, info.DstFirstMip, info.NumElements,
+            loaded_level_count, info.Filter);
+    if (SUCCEEDED(hr) && loaded_level_count < info.NumMips)
+    {
+        const uint32_t base_level = loaded_level_count - 1;
+        D3D10_BOX src_level_box, dst_level_box;
+
+        src_level_box = dst_level_box = dst_box;
+        d3d10_box_get_level(&src_level_box, base_level);
+        d3d10_box_get_level(&dst_level_box, base_level + 1);
+        if (!info.MipFilter || info.MipFilter == D3DX10_DEFAULT)
+            info.MipFilter = D3DX10_FILTER_LINEAR;
+        hr = d3d10_load_texture_from_texture(&dst_info, dst_texture, &src_level_box, info.DstFirstElement, base_level,
+                &dst_info, dst_texture, &dst_level_box, info.DstFirstElement, base_level + 1, info.NumElements,
+                (info.NumMips - loaded_level_count), info.MipFilter);
+    }
+
+    return hr;
+}
diff --git a/dlls/d3dx11_42/Makefile.in b/dlls/d3dx11_42/Makefile.in
index 965782dd57cb..a2e5edde7d2f 100644
--- a/dlls/d3dx11_42/Makefile.in
+++ b/dlls/d3dx11_42/Makefile.in
@@ -3,8 +3,6 @@ MODULE    = d3dx11_42.dll
 IMPORTLIB = d3dx11_42
 IMPORTS = d3dcompiler d3dx11
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX11"
 VER_PRODUCTVERSION      = 9,29,952,3111
 
diff --git a/dlls/d3dx11_43/Makefile.in b/dlls/d3dx11_43/Makefile.in
index 2c600281fe0c..e49a8c11c8db 100644
--- a/dlls/d3dx11_43/Makefile.in
+++ b/dlls/d3dx11_43/Makefile.in
@@ -5,8 +5,6 @@ IMPORTS   = d3dcompiler gdi32 ole32 user32
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX11"
 VER_PRODUCTVERSION      = 9,29,952,3111
 
diff --git a/dlls/d3dx11_43/tests/d3dx11.c b/dlls/d3dx11_43/tests/d3dx11.c
index a56f415dced2..bdf34daa761e 100644
--- a/dlls/d3dx11_43/tests/d3dx11.c
+++ b/dlls/d3dx11_43/tests/d3dx11.c
@@ -844,6 +844,45 @@ static const uint8_t dds_dxt10_4_4[] =
     0xf0,0xf0,0xf0,0xf0,
 };
 
+/* A 4x4 2D texture array with 2 elements and 2 mips. */
+static const uint8_t dds_2d_array_4_4[] =
+{
+    0x44,0x44,0x53,0x20,0x7c,0x00,0x00,0x00,0x01,0x10,0x00,0x00,0x04,0x00,0x00,0x00,
+    0x04,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,
+    0x04,0x00,0x00,0x00,0x44,0x58,0x31,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
+    0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,
+    0x00,0xff,0xff,0xff,
+};
+
+static const uint8_t dds_2d_array_4_4_data[] =
+{
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,
+    0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
+    0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,
+};
+
 /* 1x1 wmp image */
 static const uint8_t test_wmp[] =
 {
@@ -1674,6 +1713,7 @@ test_image[] =
         test_bmp_24bpp,      sizeof(test_bmp_24bpp),         test_bmp_24bpp_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_BMP}
     },
+    /* 5. */
     {
         test_bmp_32bpp_xrgb, sizeof(test_bmp_32bpp_xrgb),    test_bmp_32bpp_xrgb_data,
         {2, 2, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_BMP}
@@ -1694,6 +1734,7 @@ test_image[] =
         test_gif,            sizeof(test_gif),               test_gif_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_GIF}
     },
+    /* 10. */
     {
         test_tiff,           sizeof(test_tiff),              test_tiff_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_TIFF}
@@ -1714,6 +1755,7 @@ test_image[] =
         test_dds_24bpp,      sizeof(test_dds_24bpp),         test_dds_24bpp_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_DDS}
     },
+    /* 15. */
     {
         test_dds_32bpp,      sizeof(test_dds_32bpp),         test_dds_32bpp_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_DDS}
@@ -1734,6 +1776,7 @@ test_image[] =
         test_dds_dxt1,       sizeof(test_dds_dxt1),          test_dds_dxt1_data,
         {4, 4, 1, 1, 1, 0,   DXGI_FORMAT_BC1_UNORM,          D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_DDS}
     },
+    /* 20. */
     {
         test_dds_dxt1_4x8,   sizeof(test_dds_dxt1_4x8),      test_dds_dxt1_4x8_data,
         {4, 8, 1, 1, 4, 0,   DXGI_FORMAT_BC1_UNORM,          D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_DDS}
@@ -1754,6 +1797,7 @@ test_image[] =
         test_dds_dxt5,       sizeof(test_dds_dxt5),          test_dds_dxt5_data,
         {4, 2, 1, 1, 1, 0,   DXGI_FORMAT_BC3_UNORM,          D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_DDS}
     },
+    /* 25. */
     {
         test_dds_dxt5_8x8,   sizeof(test_dds_dxt5_8x8),      test_dds_dxt5_8x8_data,
         {8, 8, 1, 1, 4, 0,   DXGI_FORMAT_BC3_UNORM,          D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_DDS}
@@ -1774,6 +1818,11 @@ test_image[] =
         test_dds_volume,     sizeof(test_dds_volume),        test_dds_volume_data,
         {4, 4, 2, 1, 3, 0,   DXGI_FORMAT_BC2_UNORM,          D3D11_RESOURCE_DIMENSION_TEXTURE3D, D3DX11_IFF_DDS}
     },
+    /* 30. */
+    {
+        dds_2d_array_4_4,    sizeof(dds_2d_array_4_4),       dds_2d_array_4_4_data,
+        {4, 4, 1, 2, 2, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_DDS}
+    },
     {
         test_wmp,            sizeof(test_wmp),               test_wmp_data,
         {1, 1, 1, 1, 1, 0,   DXGI_FORMAT_R8G8B8A8_UNORM,     D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3DX11_IFF_WMP}
diff --git a/dlls/d3dx11_43/texture.c b/dlls/d3dx11_43/texture.c
index 21f5b0089648..5d4eaa4ef48b 100644
--- a/dlls/d3dx11_43/texture.c
+++ b/dlls/d3dx11_43/texture.c
@@ -372,7 +372,7 @@ static void init_load_info(const D3DX11_IMAGE_LOAD_INFO *load_info, D3DX11_IMAGE
 HRESULT load_texture_data(const void *data, SIZE_T size, D3DX11_IMAGE_LOAD_INFO *load_info,
         D3D11_SUBRESOURCE_DATA **resource_data)
 {
-    uint32_t loaded_mip_level_count, max_mip_level_count;
+    uint32_t loaded_mip_level_count, max_mip_level_count, loaded_layer_count;
     const struct pixel_format_desc *fmt_desc, *src_desc;
     struct d3dx_subresource_data *sub_rsrcs = NULL;
     D3DX11_IMAGE_INFO img_info;
@@ -396,12 +396,11 @@ HRESULT load_texture_data(const void *data, SIZE_T size, D3DX11_IMAGE_LOAD_INFO
         goto end;
     }
 
-    if ((!(img_info.MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE) || img_info.ArraySize != 6)
-            && img_info.ArraySize != 1)
+    loaded_layer_count = img_info.ArraySize;
+    if ((loaded_layer_count > 1) && (img_info.ResourceDimension == D3D11_RESOURCE_DIMENSION_TEXTURE3D))
     {
-        FIXME("img_info.ArraySize = %u not supported.\n", img_info.ArraySize);
-        hr = E_NOTIMPL;
-        goto end;
+        TRACE("Ignoring array size variable %u for 3D texture.\n", img_info.ArraySize);
+        loaded_layer_count = 1;
     }
 
     if (load_info->FirstMipLevel == D3DX11_DEFAULT || load_info->FirstMipLevel >= img_info.MipLevels)
@@ -455,13 +454,13 @@ HRESULT load_texture_data(const void *data, SIZE_T size, D3DX11_IMAGE_LOAD_INFO
     }
 
     hr = d3dx_create_subresource_data_for_texture(load_info->Width, load_info->Height, load_info->Depth,
-            load_info->MipLevels, img_info.ArraySize, fmt_desc, &sub_rsrcs);
+            load_info->MipLevels, loaded_layer_count, fmt_desc, &sub_rsrcs);
     if (FAILED(hr))
         goto end;
 
     src_desc = get_d3dx_pixel_format_info(image.format);
     loaded_mip_level_count = min(img_info.MipLevels - load_info->FirstMipLevel, load_info->MipLevels);
-    for (i = 0; i < img_info.ArraySize; ++i)
+    for (i = 0; i < loaded_layer_count; ++i)
     {
         struct volume dst_size = { load_info->Width, load_info->Height, load_info->Depth };
 
@@ -500,7 +499,7 @@ HRESULT load_texture_data(const void *data, SIZE_T size, D3DX11_IMAGE_LOAD_INFO
         }
 
         d3dx_get_mip_level_size(&base_level_size, base_level);
-        for (i = 0; i < img_info.ArraySize; ++i)
+        for (i = 0; i < loaded_layer_count; ++i)
         {
             struct volume src_size, dst_size;
 
diff --git a/dlls/d3dx9_24/Makefile.in b/dlls/d3dx9_24/Makefile.in
index bbeea11efd59..a334ba1d505f 100644
--- a/dlls/d3dx9_24/Makefile.in
+++ b/dlls/d3dx9_24/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,5,132,0
 
diff --git a/dlls/d3dx9_25/Makefile.in b/dlls/d3dx9_25/Makefile.in
index 8dbf33bb11e9..b7c0f9e9bb22 100644
--- a/dlls/d3dx9_25/Makefile.in
+++ b/dlls/d3dx9_25/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,6,168,0
 
diff --git a/dlls/d3dx9_26/Makefile.in b/dlls/d3dx9_26/Makefile.in
index b53e36620726..c8a9bc793792 100644
--- a/dlls/d3dx9_26/Makefile.in
+++ b/dlls/d3dx9_26/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,7,239,0
 
diff --git a/dlls/d3dx9_27/Makefile.in b/dlls/d3dx9_27/Makefile.in
index 49215e3e9458..38d744726cf5 100644
--- a/dlls/d3dx9_27/Makefile.in
+++ b/dlls/d3dx9_27/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,8,299,0
 
diff --git a/dlls/d3dx9_28/Makefile.in b/dlls/d3dx9_28/Makefile.in
index 97a904079577..8ce266b79182 100644
--- a/dlls/d3dx9_28/Makefile.in
+++ b/dlls/d3dx9_28/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,10,455,0
 
diff --git a/dlls/d3dx9_29/Makefile.in b/dlls/d3dx9_29/Makefile.in
index cecbb8e72eef..331613f8ba9c 100644
--- a/dlls/d3dx9_29/Makefile.in
+++ b/dlls/d3dx9_29/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,11,519,0
 
diff --git a/dlls/d3dx9_30/Makefile.in b/dlls/d3dx9_30/Makefile.in
index 3d5c4ea2c242..7f079ebb693d 100644
--- a/dlls/d3dx9_30/Makefile.in
+++ b/dlls/d3dx9_30/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,12,589,0
 
diff --git a/dlls/d3dx9_31/Makefile.in b/dlls/d3dx9_31/Makefile.in
index 510ae3d559c3..7a829b7fd8d8 100644
--- a/dlls/d3dx9_31/Makefile.in
+++ b/dlls/d3dx9_31/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,15,779,0
 
diff --git a/dlls/d3dx9_32/Makefile.in b/dlls/d3dx9_32/Makefile.in
index 0cf465904b83..5546e9b860b2 100644
--- a/dlls/d3dx9_32/Makefile.in
+++ b/dlls/d3dx9_32/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,16,843,0
 
diff --git a/dlls/d3dx9_33/Makefile.in b/dlls/d3dx9_33/Makefile.in
index 72e0b85610d0..96bdefddd5be 100644
--- a/dlls/d3dx9_33/Makefile.in
+++ b/dlls/d3dx9_33/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,18,904,15
 
diff --git a/dlls/d3dx9_34/Makefile.in b/dlls/d3dx9_34/Makefile.in
index 99f3bd79950c..7d8c9de8ff52 100644
--- a/dlls/d3dx9_34/Makefile.in
+++ b/dlls/d3dx9_34/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,19,949,46
 
diff --git a/dlls/d3dx9_35/Makefile.in b/dlls/d3dx9_35/Makefile.in
index b298c82b2d7a..613bd1e4938e 100644
--- a/dlls/d3dx9_35/Makefile.in
+++ b/dlls/d3dx9_35/Makefile.in
@@ -5,8 +5,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,19,949,1104
 
diff --git a/dlls/d3dx9_36/Makefile.in b/dlls/d3dx9_36/Makefile.in
index 29719008e688..c6a2a39ff1cb 100644
--- a/dlls/d3dx9_36/Makefile.in
+++ b/dlls/d3dx9_36/Makefile.in
@@ -4,8 +4,6 @@ IMPORTLIB = d3dx9
 IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,19,949,2111
 
diff --git a/dlls/d3dx9_37/Makefile.in b/dlls/d3dx9_37/Makefile.in
index 4e087bb193cf..fe405d175d27 100644
--- a/dlls/d3dx9_37/Makefile.in
+++ b/dlls/d3dx9_37/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,22,949,2248
 
diff --git a/dlls/d3dx9_38/Makefile.in b/dlls/d3dx9_38/Makefile.in
index 78b80d6cb881..04ed15a8d27e 100644
--- a/dlls/d3dx9_38/Makefile.in
+++ b/dlls/d3dx9_38/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,23,949,2378
 
diff --git a/dlls/d3dx9_39/Makefile.in b/dlls/d3dx9_39/Makefile.in
index bfb6fd503d3b..b1593de0f46b 100644
--- a/dlls/d3dx9_39/Makefile.in
+++ b/dlls/d3dx9_39/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,24,949,2307
 
diff --git a/dlls/d3dx9_40/Makefile.in b/dlls/d3dx9_40/Makefile.in
index b886ebf40401..010e34e312a9 100644
--- a/dlls/d3dx9_40/Makefile.in
+++ b/dlls/d3dx9_40/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,24,950,2656
 
diff --git a/dlls/d3dx9_41/Makefile.in b/dlls/d3dx9_41/Makefile.in
index 12347095ce03..5b72c7d2546c 100644
--- a/dlls/d3dx9_41/Makefile.in
+++ b/dlls/d3dx9_41/Makefile.in
@@ -4,8 +4,6 @@ IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32 wined3d
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,26,952,2844
 
diff --git a/dlls/d3dx9_42/Makefile.in b/dlls/d3dx9_42/Makefile.in
index d0284b1e50d4..8f865315e61c 100644
--- a/dlls/d3dx9_42/Makefile.in
+++ b/dlls/d3dx9_42/Makefile.in
@@ -5,8 +5,6 @@ IMPORTS   = d3d9 d3dcompiler_42 dxguid d3dxof ole32 gdi32 user32
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,27,952,3001
 
diff --git a/dlls/d3dx9_43/Makefile.in b/dlls/d3dx9_43/Makefile.in
index 98190b175dc9..b9984e5f4fc6 100644
--- a/dlls/d3dx9_43/Makefile.in
+++ b/dlls/d3dx9_43/Makefile.in
@@ -5,8 +5,6 @@ IMPORTS   = d3d9 d3dcompiler_43 dxguid d3dxof ole32 gdi32 user32
 PARENTSRC = ../d3dx9_36
 DELAYIMPORTS = windowscodecs
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine D3DX9"
 VER_PRODUCTVERSION      = 9,29,952,3111
 
diff --git a/dlls/d3dxof/parsing.c b/dlls/d3dxof/parsing.c
index 149e23711680..2e89b42a8d98 100644
--- a/dlls/d3dxof/parsing.c
+++ b/dlls/d3dxof/parsing.c
@@ -1362,10 +1362,21 @@ BOOL parse_object(parse_buffer * buf)
 {
   ULONG i;
 
+  buf->pxo->root = buf->pxo_tab;
+
+  /* Applications have been known to depend on the presence of padding before
+   * the object data.
+   * Specifically, Space Empires V reinterprets the returned object as a
+   * Delphi dynamic array and updates the value at (DWORD *)ptr - 1 for
+   * refcounting and allocation decisions. Must be >= 2. */
+  if (!check_buffer(buf, sizeof(DWORD)))
+    return FALSE;
+  *(DWORD *)(buf->pdata + buf->cur_pos_data) = 0x55555555;
+  buf->cur_pos_data += sizeof(DWORD);
+
   buf->pxo->pos_data = buf->cur_pos_data;
   buf->pxo->ptarget = NULL;
   buf->pxo->binary = FALSE;
-  buf->pxo->root = buf->pxo_tab;
 
   if (get_TOKEN(buf) != TOKEN_NAME)
     return FALSE;
diff --git a/dlls/d3dxof/tests/d3dxof.c b/dlls/d3dxof/tests/d3dxof.c
index b7b1b1cc502d..5dcf8bd203ed 100644
--- a/dlls/d3dxof/tests/d3dxof.c
+++ b/dlls/d3dxof/tests/d3dxof.c
@@ -366,6 +366,8 @@ static char object_complex[] =
 "3;;;, 0;;;, 1;;;, 2;;;,\n"
 "3;;;, 1;;;, 2;;;, 3;;;,\n"
 "3;;;, 3;;;, 1;;;, 2;;;,\n"
+/* child object for memory layout test */
+"Vector{0.0;;;, 0.0;;;, 0.0;;;}\n"
 "}\n";
 
 static char template_using_index_color_lower[] =
@@ -1005,6 +1007,119 @@ static void test_complex_object(void)
     IDirectXFile_Release(dxfile);
 }
 
+static void test_getdata_memory_layout(void)
+{
+    IDirectXFile *dxfile = NULL;
+    IDirectXFileEnumObject *enum_object, *enum_object2;
+    IDirectXFileData *root_data, *root_data2, *child_data, *child_data2;
+    IDirectXFileObject *child_obj, *child_obj2;
+    DXFILELOADMEMORY load_info;
+    HRESULT hr;
+    size_t distance;
+    DWORD root_size, child_size;
+    void *root_ptr, *child_ptr;
+    void *root_ptr2, *child_ptr2;
+    DWORD *pre_data;
+    ULONG ref;
+
+    if (!pDirectXFileCreate)
+    {
+        win_skip("DirectXFileCreate is not available\n");
+        return;
+    }
+
+    hr = pDirectXFileCreate(&dxfile);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IDirectXFile_RegisterTemplates(dxfile, templates_complex_object, sizeof(templates_complex_object) - 1);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+
+    load_info.lpMemory = object_complex;
+    load_info.dSize = sizeof(object_complex) - 1;
+    hr = IDirectXFile_CreateEnumObject(dxfile, &load_info, DXFILELOAD_FROMMEMORY, &enum_object);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IDirectXFileEnumObject_GetNextDataObject(enum_object, &root_data);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IDirectXFileData_GetData(root_data, NULL, &root_size, &root_ptr);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+    ok(root_size == 104, "Unexpected root size %lu.\n", root_size);
+
+    hr = IDirectXFileData_GetNextObject(root_data, &child_obj);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void** )&child_data);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    hr = IDirectXFileData_GetData(child_data, NULL, &child_size, &child_ptr);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+    ok(child_size == 12, "Unexpected child size %lu.\n", child_size);
+
+    distance = root_ptr > child_ptr ? (BYTE *)root_ptr - (BYTE *)child_ptr : (BYTE *)child_ptr - (BYTE *)root_ptr;
+    todo_wine ok(distance > root_size + child_size, "Unexpected distance %Iu.\n", distance);
+
+    pre_data = (DWORD *)root_ptr - 1;
+    ok(*pre_data >= 2, "Unexpected value %#lx at root_ptr - 4.\n", *pre_data);
+    pre_data = (DWORD *)child_ptr - 1;
+    ok(*pre_data >= 2, "Unexpected value %#lx at child_ptr - 4.\n", *pre_data);
+
+    hr = IDirectXFileData_GetData(root_data, NULL, &root_size, &root_ptr2);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+    hr = IDirectXFileData_GetData(child_data, NULL, &child_size, &child_ptr2);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+    ok(root_ptr == root_ptr2, "Unexpected root_ptr %p, root_ptr2 %p\n", root_ptr, root_ptr2);
+    ok(child_ptr == child_ptr2, "Unexpected child_ptr %p, child_ptr2 %p\n", child_ptr, child_ptr2);
+
+    hr = IDirectXFile_CreateEnumObject(dxfile, &load_info, DXFILELOAD_FROMMEMORY, &enum_object2);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+    ok(enum_object != enum_object2, "Unexpected enum_object %p, enum_object2 %p\n", enum_object, enum_object2);
+
+    hr = IDirectXFileEnumObject_GetNextDataObject(enum_object2, &root_data2);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+    ok(root_data != root_data2, "Unexpected root_data %p, root_data2 %p\n", root_data, root_data2);
+    hr = IDirectXFileData_GetData(root_data2, NULL, &root_size, &root_ptr2);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+    ok(root_ptr != root_ptr2, "Unexpected root_ptr %p, root_ptr2 %p\n", root_ptr, root_ptr2);
+    ok(!memcmp(root_ptr, root_ptr2, root_size), "root node contents differ\n");
+
+    hr = IDirectXFileData_GetNextObject(root_data2, &child_obj2);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+    ok(child_obj != child_obj2, "child_obj %p, child_obj2 %p\n", child_obj, child_obj2);
+    hr = IDirectXFileObject_QueryInterface(child_obj2, &IID_IDirectXFileData, (void** )&child_data2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(child_data != child_data2, "child_data %p, child_data2 %p\n", child_data, child_data2);
+    hr = IDirectXFileData_GetData(child_data2, NULL, &child_size, &child_ptr2);
+    ok(hr == DXFILE_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!memcmp(child_ptr, child_ptr2, child_size), "child node contents differ\n");
+
+    *(DWORD *)child_ptr = 0x12345678;
+    ok(!memcmp(root_ptr, root_ptr2, root_size), "root node contents differ\n");
+    ok(memcmp(child_ptr, child_ptr2, child_size), "child node contents match\n");
+
+    todo_wine
+    {
+        ref = IDirectXFileData_Release(child_data2);
+        ok(ref == 3, "Unexpected refcount %lu.\n", ref);
+        ref = IDirectXFileData_Release(child_obj2);
+        ok(ref == 2, "Unexpected refcount %lu.\n", ref);
+    }
+    ref = IDirectXFileData_Release(root_data2);
+    ok(ref == 1, "Unexpected refcount %lu.\n", ref);
+    ref = IDirectXFileEnumObject_Release(enum_object2);
+    ok(!ref, "Unexpected refcount %lu.\n", ref);
+
+    ref = IDirectXFileEnumObject_Release(enum_object);
+    ok(!ref, "Unexpected refcount %lu.\n", ref);
+    ref = IDirectXFileData_Release(root_data);
+    ok(!ref, "Unexpected refcount %lu.\n", ref);
+    ref = IDirectXFileData_Release(child_data);
+    ok(ref == 1, "Unexpected refcount %lu.\n", ref);
+    ref = IDirectXFileObject_Release(child_obj);
+    ok(!ref, "Unexpected refcount %lu.\n", ref);
+    ref = IDirectXFile_Release(dxfile);
+    ok(!ref, "Unexpected refcount %lu.\n", ref);
+}
+
 static void test_standard_templates(void)
 {
     IDirectXFile *dxfile = NULL;
@@ -1061,6 +1176,7 @@ START_TEST(d3dxof)
     test_syntax();
     test_syntax_semicolon_comma();
     test_complex_object();
+    test_getdata_memory_layout();
     test_standard_templates();
     test_type_index_color();
 
diff --git a/dlls/dcomp/Makefile.in b/dlls/dcomp/Makefile.in
index 11187b521896..3c14125a2ff6 100644
--- a/dlls/dcomp/Makefile.in
+++ b/dlls/dcomp/Makefile.in
@@ -1,7 +1,5 @@
 MODULE    = dcomp.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_PRODUCTVERSION = 10,0,14393,0
 
 SOURCES = \
diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c
index b5c53829a5b1..d4cc0cdb64b4 100644
--- a/dlls/dsound/dsound.c
+++ b/dlls/dsound/dsound.c
@@ -134,7 +134,6 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice)
         return DSERR_OUTOFMEMORY;
     }
 
-    device->ref            = 1;
     device->priolevel      = DSSCL_NORMAL;
     device->stopped        = 1;
     device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE);
@@ -187,14 +186,14 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice)
     return DS_OK;
 }
 
-static ULONG DirectSoundDevice_Release(DirectSoundDevice * device)
+static void DirectSoundDevice_destroy(DirectSoundDevice *device)
 {
     HRESULT hr;
-    ULONG ref = InterlockedDecrement(&(device->ref));
-    TRACE("(%p) ref %ld\n", device, ref);
-    if (!ref) {
         int i;
 
+        TRACE("(%p)\n", device);
+
+        InterlockedExchange(&device->terminated, TRUE);
         SetEvent(device->sleepev);
         if (device->thread) {
             WaitForSingleObject(device->thread, INFINITE);
@@ -203,10 +202,6 @@ static ULONG DirectSoundDevice_Release(DirectSoundDevice * device)
         if (device->mta_cookie)
             CoDecrementMTAUsage(device->mta_cookie);
 
-        EnterCriticalSection(&DSOUND_renderers_lock);
-        list_remove(&device->entry);
-        LeaveCriticalSection(&DSOUND_renderers_lock);
-
         /* It is allowed to release this object even when buffers are playing */
         if (device->buffers) {
             WARN("%d secondary buffers not released\n", device->nrofbuffers);
@@ -236,8 +231,6 @@ static ULONG DirectSoundDevice_Release(DirectSoundDevice * device)
         DeleteCriticalSection(&device->mixlock);
         TRACE("(%p) released\n", device);
         free(device);
-    }
-    return ref;
 }
 
 static HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcGUID)
@@ -272,13 +265,10 @@ static HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGU
     if(FAILED(hr))
         return hr;
 
-    EnterCriticalSection(&DSOUND_renderers_lock);
-
     hr = DirectSoundDevice_Create(&device);
     if(FAILED(hr)){
         WARN("DirectSoundDevice_Create failed\n");
         IMMDevice_Release(mmdevice);
-        LeaveCriticalSection(&DSOUND_renderers_lock);
         return hr;
     }
 
@@ -291,7 +281,6 @@ static HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGU
     if (FAILED(hr))
     {
         free(device);
-        LeaveCriticalSection(&DSOUND_renderers_lock);
         IMMDevice_Release(mmdevice);
         WARN("DSOUND_ReopenDevice failed: %08lx\n", hr);
         return hr;
@@ -328,9 +317,6 @@ static HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGU
     SetThreadPriority(device->thread, THREAD_PRIORITY_TIME_CRITICAL);
 
     *ppDevice = device;
-    list_add_tail(&DSOUND_renderers, &device->entry);
-
-    LeaveCriticalSection(&DSOUND_renderers_lock);
 
     return hr;
 }
@@ -611,7 +597,7 @@ void DirectSoundDevice_RemoveBuffer(DirectSoundDevice * device, IDirectSoundBuff
 static void directsound_destroy(IDirectSoundImpl *This)
 {
     if (This->device)
-        DirectSoundDevice_Release(This->device);
+        DirectSoundDevice_destroy(This->device);
     TRACE("(%p) released\n", This);
     free(This);
 }
diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c
index 8936b437ba2b..e2b9b81beef1 100644
--- a/dlls/dsound/dsound_main.c
+++ b/dlls/dsound/dsound_main.c
@@ -63,16 +63,6 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
 
-struct list DSOUND_renderers = LIST_INIT(DSOUND_renderers);
-CRITICAL_SECTION DSOUND_renderers_lock;
-static CRITICAL_SECTION_DEBUG DSOUND_renderers_lock_debug =
-{
-    0, 0, &DSOUND_renderers_lock,
-    { &DSOUND_renderers_lock_debug.ProcessLocksList, &DSOUND_renderers_lock_debug.ProcessLocksList },
-      0, 0, { (DWORD_PTR)(__FILE__ ": DSOUND_renderers_lock") }
-};
-CRITICAL_SECTION DSOUND_renderers_lock = { &DSOUND_renderers_lock_debug, -1, 0, 0, 0, 0 };
-
 /* Some applications expect the GUID pointers emitted from DirectSoundCaptureEnumerate to remain
  * valid at least until the next time DirectSoundCaptureEnumerate is called, so we store them in
  * these dynamically allocated arrays. */
@@ -789,8 +779,6 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved)
         GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)hInstDLL, &hInstDLL);
         break;
     case DLL_PROCESS_DETACH:
-        if (lpvReserved) break;
-        DeleteCriticalSection(&DSOUND_renderers_lock);
         break;
     }
     return TRUE;
diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h
index 75279dacf87d..244d88f7538b 100644
--- a/dlls/dsound/dsound_private.h
+++ b/dlls/dsound/dsound_private.h
@@ -70,14 +70,13 @@ typedef struct DSFilter {
  */
 struct DirectSoundDevice
 {
-    LONG                        ref;
-
     GUID                        guid;
     DSCAPS                      drvcaps;
     DWORD                       priolevel, sleeptime;
     PWAVEFORMATEX               pwfx, primary_pwfx;
     LPBYTE                      buffer;
     DWORD                       writelead, buflen, ac_frames, frag_frames, playpos, pad, stopped;
+    LONG                        terminated;
     int                         nrofbuffers;
     IDirectSoundBufferImpl**    buffers;
     SRWLOCK                     buffer_list_lock;
@@ -106,7 +105,6 @@ struct DirectSoundDevice
     IAudioRenderClient *render;
 
     HANDLE sleepev, thread;
-    struct list entry;
 };
 
 /* reference counted buffer memory for duplicated buffer memory */
@@ -251,9 +249,6 @@ HRESULT IDirectSoundCaptureImpl_Create(IUnknown *outer_unk, REFIID riid, void **
 #define STATE_CAPTURING 2
 #define STATE_STOPPING  3
 
-extern CRITICAL_SECTION DSOUND_renderers_lock;
-extern struct list DSOUND_renderers;
-
 extern GUID *DSOUND_renderer_guids;
 extern GUID *DSOUND_capture_guids;
 
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index 45e37c5bcb1a..1be5b5a23858 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -703,24 +703,11 @@ static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, float *mix_buffer, DWORD
 	return primary_done;
 }
 
-/**
- * For a DirectSoundDevice, go through all the currently playing buffers and
- * mix them in to the device buffer.
- *
- * frames = the maximum amount to mix into the primary buffer
- * all_stopped = reports back if all buffers have stopped
- *
- * Returns:  the length beyond the writepos that was mixed to.
- */
-
-static void DSOUND_MixToPrimary(const DirectSoundDevice *device, float *mix_buffer, DWORD frames, BOOL *all_stopped)
+static void DSOUND_MixToPrimary(const DirectSoundDevice *device, float *mix_buffer, DWORD frames)
 {
 	INT i;
 	IDirectSoundBufferImpl	*dsb;
 
-	/* unless we find a running buffer, all have stopped */
-	*all_stopped = TRUE;
-
 	TRACE("(frames %ld)\n", frames);
 	for (i = 0; i < device->nrofbuffers; i++) {
 		dsb = device->buffers[i];
@@ -738,8 +725,6 @@ static void DSOUND_MixToPrimary(const DirectSoundDevice *device, float *mix_buff
 
 				/* mix next buffer into the main buffer */
 				DSOUND_MixOne(dsb, mix_buffer, frames);
-
-				*all_stopped = FALSE;
 			}
 			ReleaseSRWLockShared(&dsb->lock);
 		}
@@ -826,7 +811,6 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
 		frames = device->frag_frames * 3;
 
 	if (device->priolevel != DSSCL_WRITEPRIMARY) {
-		BOOL all_stopped = FALSE;
 		int nfiller;
 		void *buffer = NULL;
 
@@ -848,12 +832,12 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
 		memset(buffer, nfiller, frames * block);
 
 		if (!device->normfunction)
-			DSOUND_MixToPrimary(device, buffer, frames, &all_stopped);
+			DSOUND_MixToPrimary(device, buffer, frames);
 		else {
 			memset(device->buffer, nfiller, device->buflen);
 
 			/* do the mixing */
-			DSOUND_MixToPrimary(device, (float*)device->buffer, frames, &all_stopped);
+			DSOUND_MixToPrimary(device, (float*)device->buffer, frames);
 
 			device->normfunction(device->buffer, buffer, frames * device->pwfx->nChannels);
 		}
@@ -888,24 +872,19 @@ DWORD CALLBACK DSOUND_mixthread(void *p)
 	SetThreadDescription(GetCurrentThread(), L"wine_dsound_mixer");
         _controlfp_s(NULL, _DN_FLUSH, _MCW_DN);
 
-	while (dev->ref) {
-		DWORD ret;
-
-		/*
-		 * Some audio drivers are retarded and won't fire after being
-		 * stopped, add a timeout to handle this.
-		 */
-		ret = WaitForSingleObject(dev->sleepev, dev->sleeptime);
-		if (ret == WAIT_FAILED)
-			WARN("wait returned error %lu %08lx!\n", GetLastError(), GetLastError());
-		else if (ret != WAIT_OBJECT_0)
-			WARN("wait returned %08lx!\n", ret);
-		if (!dev->ref)
-			break;
-
-		AcquireSRWLockShared(&dev->buffer_list_lock);
-		DSOUND_PerformMix(dev);
-		ReleaseSRWLockShared(&dev->buffer_list_lock);
-	}
+        for (;;)
+        {
+            DWORD ret = WaitForSingleObject(dev->sleepev, dev->sleeptime);
+            if (ret == WAIT_FAILED)
+                WARN("wait returned error %lu %08lx!\n", GetLastError(), GetLastError());
+            else if (ret != WAIT_OBJECT_0)
+                WARN("wait returned %08lx!\n", ret);
+            if (dev->terminated)
+                break;
+
+            AcquireSRWLockShared(&dev->buffer_list_lock);
+            DSOUND_PerformMix(dev);
+            ReleaseSRWLockShared(&dev->buffer_list_lock);
+        }
 	return 0;
 }
diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c
index f6c2fe8146e0..dc434949c42c 100644
--- a/dlls/dwrite/analyzer.c
+++ b/dlls/dwrite/analyzer.c
@@ -308,6 +308,9 @@ system_fallback_config[] =
     { "1C00-1C4F",              L"Noto Sans Lepcha" },
     { "1C50-1C7F",              L"Noto Sans Ol Chiki" },
 
+    /* Miscellaneous Symbols - 2600-26FF */
+    { "2600-26FF",              L"Noto Sans Symbols2, Noto Sans Symbols" },
+
     /* Dingbats - 2700-27BF */
     { "2700-27BF",              L"Noto Sans Symbols2, Noto Sans Symbols 2" },
 
diff --git a/dlls/dxcore/Makefile.in b/dlls/dxcore/Makefile.in
index 147216555e96..7c7e60f0969c 100644
--- a/dlls/dxcore/Makefile.in
+++ b/dlls/dxcore/Makefile.in
@@ -1,7 +1,5 @@
 MODULE  = dxcore.dll
 IMPORTS = wined3d
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	dxcore.c
diff --git a/dlls/dxva2/Makefile.in b/dlls/dxva2/Makefile.in
index ed88d3621f19..de43b161ccd5 100644
--- a/dlls/dxva2/Makefile.in
+++ b/dlls/dxva2/Makefile.in
@@ -2,7 +2,5 @@ MODULE    = dxva2.dll
 IMPORTS   = uuid ole32
 IMPORTLIB = dxva2
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	main.c
diff --git a/dlls/fltlib/Makefile.in b/dlls/fltlib/Makefile.in
index 3ba4b8ad43d6..50760a220f4c 100644
--- a/dlls/fltlib/Makefile.in
+++ b/dlls/fltlib/Makefile.in
@@ -1,7 +1,5 @@
 MODULE    = fltlib.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine Filter Library"
 VER_PRODUCTVERSION      = 5,1,2600,5512
 
diff --git a/dlls/fwpuclnt/Makefile.in b/dlls/fwpuclnt/Makefile.in
index 9efa6b30cc2d..0a0948fbfbaa 100644
--- a/dlls/fwpuclnt/Makefile.in
+++ b/dlls/fwpuclnt/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = fwpuclnt.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	main.c
diff --git a/dlls/gdi32/uniscribe/opentype.c b/dlls/gdi32/uniscribe/opentype.c
index b2a72b8c3ffa..2e6d5e807460 100644
--- a/dlls/gdi32/uniscribe/opentype.c
+++ b/dlls/gdi32/uniscribe/opentype.c
@@ -2091,8 +2091,14 @@ static BOOL GPOS_apply_MarkToMark(const OT_LookupTable *look, const SCRIPT_ANALY
             if (mark_index != -1)
             {
                 int mark2_index;
+                unsigned int prev_glyph_index = glyph_index - write_dir;
+                if (prev_glyph_index >= glyph_count)
+                {
+                    TRACE("Mark looking outside range\n");
+                    return FALSE;
+                }
                 offset = GET_BE_WORD(mmpf1->Mark2Coverage);
-                mark2_index = GSUB_is_glyph_covered((const BYTE*)mmpf1+offset, glyphs[glyph_index - write_dir]);
+                mark2_index = GSUB_is_glyph_covered((const BYTE*)mmpf1+offset, glyphs[prev_glyph_index]);
                 if (mark2_index != -1)
                 {
                     const GPOS_MarkArray *ma;
@@ -2104,7 +2110,7 @@ static BOOL GPOS_apply_MarkToMark(const OT_LookupTable *look, const SCRIPT_ANALY
                     int mark2record_size;
                     POINT mark2_pt;
                     POINT mark_pt;
-                    TRACE("Mark %x(%i) and Mark2 %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[glyph_index - write_dir], mark2_index);
+                    TRACE("Mark %x(%i) and Mark2 %x(%i)\n",glyphs[glyph_index], mark_index, glyphs[prev_glyph_index], mark2_index);
                     offset = GET_BE_WORD(mmpf1->Mark1Array);
                     ma = (const GPOS_MarkArray*)((const BYTE*)mmpf1 + offset);
                     if (mark_index > GET_BE_WORD(ma->MarkCount))
@@ -2206,8 +2212,12 @@ static unsigned int GPOS_apply_ContextPos(const ScriptCache *script_cache, const
 
                     for (l = 0; l < g_count-1; l++)
                     {
-                        int g_class = OT_get_glyph_class(glyph_class_table, glyphs[glyph_index + (write_dir * (l+1))]);
-                        if (g_class != GET_BE_WORD(pr->Class[l])) break;
+                        g = glyph_index + (write_dir * (l+1));
+                        if (g < glyph_count)
+                        {
+                            int g_class = OT_get_glyph_class(glyph_class_table, glyphs[g]);
+                            if (g_class != GET_BE_WORD(pr->Class[l])) break;
+                        }
                     }
 
                     if (l < g_count-1)
@@ -2319,9 +2329,12 @@ static unsigned int GPOS_apply_ChainContextPos(const ScriptCache *script_cache,
 
             for (k = 0; k < backtrack_count; ++k)
             {
+                unsigned int prev_glyph_index;
                 offset = GET_BE_WORD(backtrack->Coverage[k]);
-                if (GSUB_is_glyph_covered((const BYTE *)backtrack + offset,
-                        glyphs[glyph_index + (dirBacktrack * (k + 1))]) == -1)
+                prev_glyph_index = glyph_index + (dirBacktrack * (k + 1));
+                if (prev_glyph_index >= glyph_count)
+                    continue;
+                if (GSUB_is_glyph_covered((const BYTE *)backtrack + offset, glyphs[prev_glyph_index]) == -1)
                     break;
             }
             if (k != backtrack_count)
@@ -2330,9 +2343,12 @@ static unsigned int GPOS_apply_ChainContextPos(const ScriptCache *script_cache,
 
             for (k = 0; k < input_count; ++k)
             {
+                unsigned int next_glyph_index = glyph_index + (write_dir * k);
+                if (next_glyph_index >= glyph_count)
+                    continue;
                 offset = GET_BE_WORD(input->Coverage[k]);
                 if (GSUB_is_glyph_covered((const BYTE *)backtrack + offset,
-                        glyphs[glyph_index + (write_dir * k)]) == -1)
+                        glyphs[next_glyph_index]) == -1)
                     break;
             }
             if (k != input_count)
@@ -2341,9 +2357,12 @@ static unsigned int GPOS_apply_ChainContextPos(const ScriptCache *script_cache,
 
             for (k = 0; k < lookahead_count; ++k)
             {
+                unsigned int next_glyph_index = glyph_index + (dirLookahead * (input_count + k));
+                if (next_glyph_index >= glyph_count)
+                    continue;
                 offset = GET_BE_WORD(lookahead->Coverage[k]);
                 if (GSUB_is_glyph_covered((const BYTE *)backtrack + offset,
-                        glyphs[glyph_index + (dirLookahead * (input_count + k))]) == -1)
+                        glyphs[next_glyph_index]) == -1)
                     break;
             }
             if (k != lookahead_count)
@@ -2446,6 +2465,12 @@ static unsigned int GPOS_apply_lookup(const ScriptCache *script_cache, const OUT
             int index_offset;
             int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
             int offset_sign = (analysis->fRTL && analysis->fLogicalOrder) ? -1 : 1;
+            unsigned int next_glyph_index = glyph_index + write_dir;
+            if (next_glyph_index >= glyph_count)
+            {
+                WARN("Skipping invalid glyph index %u, write dir %d.\n", glyph_index, write_dir);
+                break;
+            }
 
             index_offset = GPOS_apply_PairAdjustment(look, analysis, glyphs,
                     glyph_index, glyph_count, ppem, adjust, advance);
@@ -2463,13 +2488,13 @@ static unsigned int GPOS_apply_lookup(const ScriptCache *script_cache, const OUT
             if (adjust[1].x || adjust[1].y)
             {
                 GPOS_convert_design_units_to_device(lpotm, lplogfont, adjust[1].x, adjust[1].y, &devX, &devY);
-                pGoffset[glyph_index + write_dir].du += round(devX) * offset_sign;
-                pGoffset[glyph_index + write_dir].dv += round(devY);
+                pGoffset[next_glyph_index].du += round(devX) * offset_sign;
+                pGoffset[next_glyph_index].dv += round(devY);
             }
             if (advance[1].x || advance[1].y)
             {
                 GPOS_convert_design_units_to_device(lpotm, lplogfont, advance[1].x, advance[1].y, &devX, &devY);
-                piAdvance[glyph_index + write_dir] += round(devX);
+                piAdvance[next_glyph_index] += round(devX);
             }
             return index_offset;
         }
@@ -2479,13 +2504,19 @@ static unsigned int GPOS_apply_lookup(const ScriptCache *script_cache, const OUT
             POINT desU = {0,0};
             double devX, devY;
             int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
+            unsigned int next_glyph_index = glyph_index + write_dir;
+            if (next_glyph_index >= glyph_count)
+            {
+                WARN("Skipping invalid glyph index %u, write dir %d.\n", glyph_index, write_dir);
+                break;
+            }
 
             GPOS_apply_CursiveAttachment(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU);
             if (desU.x || desU.y)
             {
                 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY);
                 /* Windows does not appear to apply X offsets here */
-                pGoffset[glyph_index].dv = round(devY) + pGoffset[glyph_index+write_dir].dv;
+                pGoffset[glyph_index].dv = round(devY) + pGoffset[next_glyph_index].dv;
             }
             break;
         }
@@ -2529,12 +2560,19 @@ static unsigned int GPOS_apply_lookup(const ScriptCache *script_cache, const OUT
             double devX, devY;
             POINT desU = {0,0};
             int write_dir = (analysis->fRTL && !analysis->fLogicalOrder) ? -1 : 1;
+            unsigned int prev_glyph_index = glyph_index - write_dir;
+            if (prev_glyph_index >= glyph_count)
+            {
+                WARN("Skipping invalid glyph index %u, write dir %d.\n", glyph_index, write_dir);
+                break;
+            }
+
             if (GPOS_apply_MarkToMark(look, analysis, glyphs, glyph_index, glyph_count, ppem, &desU))
             {
                 GPOS_convert_design_units_to_device(lpotm, lplogfont, desU.x, desU.y, &devX, &devY);
                 if (analysis->fRTL && analysis->fLogicalOrder) devX *= -1;
-                pGoffset[glyph_index].du = round(devX) + pGoffset[glyph_index - write_dir].du;
-                pGoffset[glyph_index].dv = round(devY) + pGoffset[glyph_index - write_dir].dv;
+                pGoffset[glyph_index].du = round(devX) + pGoffset[prev_glyph_index].du;
+                pGoffset[glyph_index].dv = round(devY) + pGoffset[prev_glyph_index].dv;
             }
             break;
         }
diff --git a/dlls/hal/hal.c b/dlls/hal/hal.c
index 128d2ef27ca1..ced2fb21ee32 100644
--- a/dlls/hal/hal.c
+++ b/dlls/hal/hal.c
@@ -162,7 +162,7 @@ void WINAPI WRITE_PORT_ULONG(ULONG *port, ULONG value)
 {
     FIXME("(%p %ld) stub!\n", port, value);
 }
-#endif /* __i386__ || __arm__ || __arm64__ */
+#endif /* __i386__ || __arm__ || __aarch64__ */
 
 ULONG WINAPI HalGetBusData(BUS_DATA_TYPE BusDataType, ULONG BusNumber, ULONG SlotNumber, PVOID Buffer, ULONG Length)
 {
diff --git a/dlls/hnetcfg/policy.c b/dlls/hnetcfg/policy.c
index 5031fdcdd927..88de6ed54040 100644
--- a/dlls/hnetcfg/policy.c
+++ b/dlls/hnetcfg/policy.c
@@ -783,18 +783,136 @@ static HRESULT WINAPI netfw_rules_Item(
     return E_NOTIMPL;
 }
 
+static HRESULT NewEnum_create(fw_rules *rules, IUnknown **obj);
+
+typedef struct
+{
+    IEnumVARIANT IEnumVARIANT_iface;
+    LONG ref, pos;
+    fw_rules *rules;
+} EnumVARIANT;
+
+static inline EnumVARIANT *impl_from_IEnumVARIANT(IEnumVARIANT *iface)
+{
+    return CONTAINING_RECORD(iface, EnumVARIANT, IEnumVARIANT_iface);
+}
+
+static HRESULT WINAPI enumvar_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **obj)
+{
+    if (!riid || !obj) return E_INVALIDARG;
+
+    TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), obj);
+
+    if (IsEqualGUID(riid, &IID_IEnumVARIANT) ||
+        IsEqualGUID(riid, &IID_IUnknown))
+    {
+        IEnumVARIANT_AddRef(iface);
+        *obj = iface;
+        return S_OK;
+    }
+
+    FIXME("interface %s is not implemented\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI enumvar_AddRef(IEnumVARIANT *iface)
+{
+    EnumVARIANT *enumvar = impl_from_IEnumVARIANT(iface);
+    return InterlockedIncrement(&enumvar->ref);
+}
+
+static ULONG WINAPI enumvar_Release(IEnumVARIANT *iface)
+{
+    EnumVARIANT *enumvar = impl_from_IEnumVARIANT(iface);
+    LONG ref = InterlockedDecrement(&enumvar->ref);
+
+    if (!ref)
+    {
+        TRACE("destroying %p\n", iface);
+        INetFwRules_Release(&enumvar->rules->INetFwRules_iface);
+        free(enumvar);
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI enumvar_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *var, ULONG *fetched)
+{
+    FIXME("%p,%lu,%p,%p\n", iface, celt, var, fetched);
+
+    return  S_FALSE;
+}
+
+static HRESULT WINAPI enumvar_Skip(IEnumVARIANT *iface, ULONG celt)
+{
+    FIXME("%p,%lu\n", iface, celt);
+
+    return S_FALSE;
+}
+
+static HRESULT WINAPI enumvar_Reset(IEnumVARIANT *iface)
+{
+    EnumVARIANT *enumvar = impl_from_IEnumVARIANT(iface);
+
+    TRACE("%p\n", iface);
+
+    enumvar->pos = 0;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI enumvar_Clone(IEnumVARIANT *iface, IEnumVARIANT **penum)
+{
+    EnumVARIANT *enumvar = impl_from_IEnumVARIANT(iface);
+
+    FIXME("%p,%p\n", iface, penum);
+
+    return NewEnum_create(enumvar->rules, (IUnknown **)penum);
+}
+
+static const struct IEnumVARIANTVtbl EnumVARIANT_vtbl =
+{
+    enumvar_QueryInterface,
+    enumvar_AddRef,
+    enumvar_Release,
+    enumvar_Next,
+    enumvar_Skip,
+    enumvar_Reset,
+    enumvar_Clone
+};
+
+static HRESULT NewEnum_create(fw_rules *rules, IUnknown **obj)
+{
+    EnumVARIANT *enumvar;
+
+    enumvar = malloc(sizeof(*enumvar));
+    if (!enumvar) return E_OUTOFMEMORY;
+
+    enumvar->IEnumVARIANT_iface.lpVtbl = &EnumVARIANT_vtbl;
+    enumvar->ref = 1;
+    enumvar->pos = 0;
+    enumvar->rules = rules;
+    INetFwRules_AddRef(&rules->INetFwRules_iface);
+
+    *obj = (IUnknown *)&enumvar->IEnumVARIANT_iface;
+
+    TRACE("created %p\n", *obj);
+
+    return S_OK;
+}
+
 static HRESULT WINAPI netfw_rules_get__NewEnum(
     INetFwRules *iface,
     IUnknown **newEnum)
 {
     fw_rules *This = impl_from_INetFwRules( iface );
 
-    FIXME("%p, %p\n", This, newEnum);
+    TRACE("%p, %p\n", This, newEnum);
 
     if (!newEnum) return E_POINTER;
-    *newEnum = NULL;
 
-    return E_NOTIMPL;
+    return NewEnum_create(This, newEnum);
 }
 
 static const struct INetFwRulesVtbl fw_rules_vtbl =
diff --git a/dlls/hnetcfg/tests/policy.c b/dlls/hnetcfg/tests/policy.c
index 9140cfdf1fd5..b3f7e1f7e7ae 100644
--- a/dlls/hnetcfg/tests/policy.c
+++ b/dlls/hnetcfg/tests/policy.c
@@ -22,7 +22,6 @@
 #include "ole2.h"
 #include "oleauto.h"
 #include "olectl.h"
-#include "dispex.h"
 
 #include "wine/test.h"
 
@@ -40,6 +39,7 @@ static void test_policy2_rules(INetFwPolicy2 *policy2)
     HRESULT hr;
     INetFwRules *rules, *rules2;
     INetFwServiceRestriction *restriction;
+    IUnknown *rulesenum;
 
     hr = INetFwPolicy2_QueryInterface(policy2, &IID_INetFwRules, (void**)&rules);
     ok(hr == E_NOINTERFACE, "got 0x%08lx\n", hr);
@@ -69,6 +69,19 @@ static void test_policy2_rules(INetFwPolicy2 *policy2)
     hr = INetFwRules_get__NewEnum(rules, NULL);
     ok(hr == E_POINTER, "got %08lx\n", hr);
 
+    hr = INetFwRules_get__NewEnum(rules, &rulesenum);
+    ok(hr == S_OK, "got %08lx\n", hr);
+    if(rulesenum)
+    {
+        IEnumVARIANT *enumvar;
+
+        hr = IUnknown_QueryInterface(rulesenum, &IID_IEnumVARIANT, (void**)&enumvar);
+        ok(hr == S_OK, "got %08lx\n", hr);
+        IEnumVARIANT_Release(enumvar);
+
+        IUnknown_Release(rulesenum);
+    }
+
     INetFwRules_Release(rules);
     INetFwRules_Release(rules2);
 }
diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c
index c4d71856e687..3cf7c1ed00a3 100644
--- a/dlls/imm32/ime.c
+++ b/dlls/imm32/ime.c
@@ -419,7 +419,7 @@ static UINT ime_set_comp_string( HIMC himc, LPARAM lparam )
 
 static LRESULT ime_ui_notify( HIMC himc, HWND hwnd, WPARAM wparam, LPARAM lparam )
 {
-    TRACE( "himc %p, hwnd %p, wparam %s, lparam %#Ix\n", hwnd, himc, debugstr_imn(wparam), lparam );
+    TRACE( "himc %p, hwnd %p, wparam %s, lparam %#Ix\n", himc, hwnd, debugstr_imn(wparam), lparam );
 
     switch (wparam)
     {
diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c
index 0b2814da2cb6..43f56268f921 100644
--- a/dlls/imm32/imm.c
+++ b/dlls/imm32/imm.c
@@ -760,8 +760,9 @@ static void input_context_init( INPUTCONTEXT *ctx )
 static void IMM_FreeThreadData(void)
 {
     struct coinit_spy *spy;
+    HIMC default_imc = (HIMC)NtUserGetThreadState( UserThreadStateDefaultInputContext );
 
-    free_input_context_data( UlongToHandle( NtUserGetThreadInfo()->default_imc ) );
+    free_input_context_data( default_imc );
     if ((spy = get_thread_coinit_spy())) IInitializeSpy_Release( &spy->IInitializeSpy_iface );
 }
 
@@ -932,9 +933,8 @@ static struct imc *get_imc_data( HIMC handle )
 
 static struct imc *default_input_context(void)
 {
-    UINT *himc = &NtUserGetThreadInfo()->default_imc;
-    if (!*himc) *himc = (UINT_PTR)NtUserCreateInputContext( 0 );
-    return get_imc_data( (HIMC)(UINT_PTR)*himc );
+    HIMC himc = (HIMC)NtUserGetThreadState( UserThreadStateDefaultInputContext );
+    return get_imc_data( himc );
 }
 
 static HWND get_ime_ui_window(void)
@@ -984,7 +984,9 @@ static BOOL IMM_DestroyContext(HIMC hIMC)
  */
 BOOL WINAPI ImmDestroyContext(HIMC hIMC)
 {
-    if ((UINT_PTR)hIMC == NtUserGetThreadInfo()->default_imc) return FALSE;
+    HIMC default_imc = (HIMC)NtUserGetThreadState( UserThreadStateDefaultInputContext );
+
+    if (hIMC == default_imc) return FALSE;
     if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
     return IMM_DestroyContext(hIMC);
 }
diff --git a/dlls/kernel32/tests/debugger.c b/dlls/kernel32/tests/debugger.c
index 478eaee97936..489cb535b723 100644
--- a/dlls/kernel32/tests/debugger.c
+++ b/dlls/kernel32/tests/debugger.c
@@ -2435,6 +2435,10 @@ static void test_OutputDebugString(void)
     pOutputDebugStringA("test");
     ok(GetLastError() == 0xdeadbeef, "got %ld.\n", GetLastError());
 
+    SetLastError(0xdeadbeef);
+    OutputDebugStringW(NULL);
+    ok(GetLastError() == 0xdeadbeef, "got %ld.\n", GetLastError());
+
     SetLastError(0xdeadbeef);
     OutputDebugStringW(L"test");
     ok(GetLastError() == 0xdeadbeef, "got %ld.\n", GetLastError());
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c
index d66ab2efe66b..ed6e0606f1e5 100644
--- a/dlls/kernel32/tests/file.c
+++ b/dlls/kernel32/tests/file.c
@@ -4466,6 +4466,18 @@ static void test_ReplaceFileW(void)
         "ReplaceFileW: unexpected error %ld\n", GetLastError());
     DeleteFileW( replacement );
 
+    ret = GetTempFileNameW(temp_path, prefix, 0, replacement);
+    ok(ret, "GetTempFileNameW error (replacement) %ld\n", GetLastError());
+    ret = CreateDirectoryW(replaced, NULL);
+    ok(ret, "CreateDirectoryW error %ld\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = pReplaceFileW(replaced, replacement, NULL, 0, 0, 0);
+    ok(!ret, "expected failure\n");
+    ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %lu\n", GetLastError());
+    ret = RemoveDirectoryW(replaced);
+    ok(ret, "RemoveDirectoryW error %ld\n", GetLastError());
+    DeleteFileW(replacement);
+
     if (removeBackup)
     {
         ret = DeleteFileW(backup);
diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c
index a600b6b59c9b..8656859b7bb6 100644
--- a/dlls/kernelbase/debug.c
+++ b/dlls/kernelbase/debug.c
@@ -281,6 +281,7 @@ void WINAPI DECLSPEC_HOTPATCH OutputDebugStringW( LPCWSTR str )
     STRING strA;
 
     WARN( "%s\n", debugstr_w(str) );
+    if (!str) return;
 
     RtlInitUnicodeString( &strW, str );
     if (!RtlUnicodeStringToAnsiString( &strA, &strW, TRUE ))
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index 318844261439..9b66cecee6ed 100644
--- a/dlls/kernelbase/file.c
+++ b/dlls/kernelbase/file.c
@@ -2766,7 +2766,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH ReplaceFileW( const WCHAR *replaced, const WCHAR *
     RtlFreeUnicodeString(&nt_replaced_name);
     if (!set_ntstatus( status )) return FALSE;
 
-    if (info.FileAttributes & FILE_ATTRIBUTE_READONLY)
+    if (info.FileAttributes & ( FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY ))
     {
         SetLastError( ERROR_ACCESS_DENIED );
         return FALSE;
diff --git a/dlls/kernelbase/kernelbase.rgs b/dlls/kernelbase/kernelbase.rgs
index 1258f75d6cb7..03eb8aa446cc 100644
--- a/dlls/kernelbase/kernelbase.rgs
+++ b/dlls/kernelbase/kernelbase.rgs
@@ -4144,6 +4144,7 @@ HKLM
                             val 'es-ES_tradnl' = s '{0000002a-57ee-1e5c-00b4-d0000bb1e11e}'
                             val 'et' = s '{00000027-57ee-1e5c-00b4-d0000bb1e11e}'
                             val 'fa' = s '{00000041-57ee-1e5c-00b4-d0000bb1e11e}'
+                            val 'fa-AF' = s '{00000016-57ee-1e5c-00b4-d0000bb1e11e}'
                             val 'fi' = s '{0000001a-57ee-1e5c-00b4-d0000bb1e11e}'
                             val 'fr' = s '{00000003-57ee-1e5c-00b4-d0000bb1e11e}'
                             val 'fy' = s '{0000003e-57ee-1e5c-00b4-d0000bb1e11e}'
diff --git a/dlls/ksuser/Makefile.in b/dlls/ksuser/Makefile.in
index 7e0659889262..17e023d6c2f5 100644
--- a/dlls/ksuser/Makefile.in
+++ b/dlls/ksuser/Makefile.in
@@ -1,5 +1,3 @@
 MODULE    = ksuser.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_PRODUCTVERSION = 5,3,0,900
diff --git a/dlls/mf/clock.c b/dlls/mf/clock.c
index 1619f2790eba..ab889220d00b 100644
--- a/dlls/mf/clock.c
+++ b/dlls/mf/clock.c
@@ -549,24 +549,11 @@ static HRESULT clock_call_state_change(MFTIME system_time, struct clock_state_ch
     return hr;
 }
 
-static struct clock_timer *presentation_clock_next_timer(struct presentation_clock *clock, LONGLONG time)
-{
-    struct clock_timer *timer;
-
-    LIST_FOR_EACH_ENTRY(timer, &clock->timers, struct clock_timer, entry)
-    {
-        if (timer->time <= time + 50000)
-            return timer;
-    }
-
-    return NULL;
-}
-
 static void CALLBACK presentation_clock_timer_callback(IUnknown *context)
 {
     struct presentation_clock *clock = impl_from_IMFPresentationClock((IMFPresentationClock*)context);
     IMFPresentationTimeSource *time_source;
-    struct clock_timer *timer;
+    struct clock_timer *timer, *next;
     LONGLONG time, systime;
 
     EnterCriticalSection(&clock->cs);
@@ -579,8 +566,11 @@ static void CALLBACK presentation_clock_timer_callback(IUnknown *context)
             SUCCEEDED(IMFPresentationTimeSource_GetCorrelatedTime(time_source, 0, &time, &systime)))
     {
         EnterCriticalSection(&clock->cs);
-        while ( (timer = presentation_clock_next_timer(clock, time)) )
+        LIST_FOR_EACH_ENTRY_SAFE(timer, next, &clock->timers, struct clock_timer, entry)
         {
+            if (timer->time > time + 50000)
+                break;
+
             list_remove(&timer->entry);
             MFInvokeCallback(timer->result);
             IUnknown_Release(&timer->IUnknown_iface);
@@ -885,7 +875,7 @@ static HRESULT WINAPI present_clock_timer_SetTimer(IMFTimer *iface, DWORD flags,
         IMFAsyncCallback *callback, IUnknown *state, IUnknown **cancel_key)
 {
     struct presentation_clock *clock = impl_from_IMFTimer(iface);
-    struct clock_timer *clock_timer;
+    struct clock_timer *clock_timer, *cur;
     HRESULT hr;
 
     TRACE("%p, %#lx, %s, %p, %p, %p.\n", iface, flags, debugstr_time(time), callback, state, cancel_key);
@@ -909,7 +899,13 @@ static HRESULT WINAPI present_clock_timer_SetTimer(IMFTimer *iface, DWORD flags,
 
     if (SUCCEEDED(hr))
     {
-        list_add_tail(&clock->timers, &clock_timer->entry);
+        LIST_FOR_EACH_ENTRY_REV(cur, &clock->timers, struct clock_timer, entry)
+        {
+            if (cur->time <= time)
+                break;
+        }
+        list_add_after(&cur->entry, &clock_timer->entry);
+
         if (cancel_key)
         {
             *cancel_key = &clock_timer->IUnknown_iface;
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c
index 11b47871f339..5b9db4815468 100644
--- a/dlls/mf/tests/mf.c
+++ b/dlls/mf/tests/mf.c
@@ -30,6 +30,7 @@
 #include "mfapi.h"
 #include "mferror.h"
 #include "mfidl.h"
+#include "mmsystem.h"
 #include "uuids.h"
 #include "wmcodecdsp.h"
 #include "nserror.h"
@@ -3919,6 +3920,9 @@ struct test_time_source
     IMFClockStateSink IMFClockStateSink_iface;
     LONG refcount;
     LONGLONG time;
+    UINT64 gettime_call_count;
+    MFTIME gettime_first_call_time;
+    MFTIME gettime_last_call_time;
 };
 
 static struct test_time_source *test_time_source_from_IMFPresentationTimeSource(IMFPresentationTimeSource *iface)
@@ -3983,6 +3987,11 @@ static HRESULT WINAPI test_time_source_GetCorrelatedTime(IMFPresentationTimeSour
     *clock_time = test_time_source->time;
     *system_time = MFGetSystemTime();
 
+    test_time_source->gettime_last_call_time = *system_time;
+    if (!test_time_source->gettime_call_count)
+        test_time_source->gettime_first_call_time = *system_time;
+    ++test_time_source->gettime_call_count;
+
     return S_OK;
 }
 
@@ -4128,17 +4137,20 @@ static void test_presentation_clock(void)
     IMFClockStateSink test_sink = { &test_clock_sink_vtbl };
     struct test_time_source *test_time_source;
     IMFPresentationTimeSource *time_source;
-    struct test_callback *timer_callback;
     MFCLOCK_PROPERTIES props, props2;
+    IMFAsyncCallback *callbacks[10];
+    char callback_order[32] = { 0 };
+    MFTIME systime, time, duration;
+    LONGLONG callback_interval_us;
     IMFRateControl *rate_control;
     IMFPresentationClock *clock;
-    IMFAsyncCallback *callback;
     IUnknown *timer_cancel_key;
+    char *callback_order_end;
     MFSHUTDOWN_STATUS status;
     IMFShutdown *shutdown;
-    MFTIME systime, time;
     LONGLONG clock_time;
     MFCLOCK_STATE state;
+    HANDLE events[10];
     IMFTimer *timer;
     unsigned int i;
     DWORD t1, t2;
@@ -4147,6 +4159,8 @@ static void test_presentation_clock(void)
     HRESULT hr;
     BOOL thin;
 
+    timeBeginPeriod(1);
+
     hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
     ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr);
 
@@ -4365,6 +4379,11 @@ static void test_presentation_clock(void)
 
     IMFRateControl_Release(rate_control);
 
+    for (i = 0; i < ARRAY_SIZE(callbacks); ++i)
+    {
+        callbacks[i] = create_test_callback(FALSE);
+        events[i] = impl_from_IMFAsyncCallback(callbacks[i])->event;
+    }
 
     hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFTimer, (void **)&timer);
     ok(hr == S_OK, "got hr %#lx.\n", hr);
@@ -4375,20 +4394,17 @@ static void test_presentation_clock(void)
     hr = IMFPresentationClock_GetCorrelatedTime(clock, 0, &time, &systime);
     ok(hr == S_OK, "got hr %#lx.\n", hr);
 
-    callback = create_test_callback(FALSE);
-    timer_callback = impl_from_IMFAsyncCallback(callback);
-    hr = IMFTimer_SetTimer(timer, 0, 100000, callback, NULL, &timer_cancel_key);
+    hr = IMFTimer_SetTimer(timer, 0, 100000, callbacks[0], NULL, &timer_cancel_key);
     ok(hr == S_OK, "got hr %#lx.\n", hr);
 
     t1 = GetTickCount();
-    ok(WaitForSingleObject(timer_callback->event, 4000) == WAIT_OBJECT_0, "WaitForSingleObject failed.\n");
+    ok(WaitForSingleObject(events[0], 4000) == WAIT_OBJECT_0, "WaitForSingleObject failed.\n");
     t2 = GetTickCount();
 
     ok(t2 - t1 < 200, "unexpected time difference %lu.\n", t2 - t1);
 
     IUnknown_Release(timer_cancel_key);
     IMFTimer_Release(timer);
-    IMFAsyncCallback_Release(callback);
 
     hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFShutdown, (void **)&shutdown);
     ok(hr == S_OK, "Failed to get shutdown interface, hr %#lx.\n", hr);
@@ -4451,28 +4467,69 @@ static void test_presentation_clock(void)
     hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFTimer, (void **)&timer);
     ok(hr == S_OK, "got hr %#lx.\n", hr);
 
-    callback = create_test_callback(FALSE);
-    timer_callback = impl_from_IMFAsyncCallback(callback);
-    hr = IMFTimer_SetTimer(timer, 0, 1000000, callback, NULL, &timer_cancel_key);
+    hr = IMFTimer_SetTimer(timer, 0, 1000000, callbacks[0], NULL, &timer_cancel_key);
     ok(hr == S_OK, "got hr %#lx.\n", hr);
-    ok(WaitForSingleObject(timer_callback->event, 4000) == WAIT_TIMEOUT, "WaitForSingleObject should timeout.\n");
+    ok(WaitForSingleObject(events[0], 4000) == WAIT_TIMEOUT, "WaitForSingleObject should timeout.\n");
 
     /* the timer will only trigger when the time of the time source is within 5ms of the target time */
     test_time_source->time = 1000000-50001;
-    ok(WaitForSingleObject(timer_callback->event, 4000) == WAIT_TIMEOUT, "WaitForSingleObject should timeout.\n");
+    ok(WaitForSingleObject(events[0], 4000) == WAIT_TIMEOUT, "WaitForSingleObject should timeout.\n");
 
     test_time_source->time = 1000000-50000;
-    ok(WaitForSingleObject(timer_callback->event, 4000) == WAIT_OBJECT_0, "WaitForSingleObject failed.\n");
+    ok(WaitForSingleObject(events[0], 4000) == WAIT_OBJECT_0, "WaitForSingleObject failed.\n");
 
     IUnknown_Release(timer_cancel_key);
-    IMFTimer_Release(timer);
-    IMFAsyncCallback_Release(callback);
 
+    /* Check the frequency of the periodic timer of the presentation clock. */
+    test_time_source->gettime_call_count = 0;
+    Sleep(500);
+    duration = test_time_source->gettime_last_call_time - test_time_source->gettime_first_call_time;
+    callback_interval_us = duration / (test_time_source->gettime_call_count - 1) / 10;
+    ok(llabs(callback_interval_us - 15625) < 2500
+            || llabs(callback_interval_us - 10000) < 2500 /* <= w1064v1909 */,
+            "got callback interval %lld us.\n", callback_interval_us);
+
+    /* Timers fire in time order, not insertion order. */
+    callback_order_end = callback_order;
+    duration = 0;
+
+    test_time_source->time = 0;
+    for (i = ARRAY_SIZE(callbacks); i > 0; --i)
+    {
+        hr = IMFTimer_SetTimer(timer, 0, 1000000 + i * 200000, callbacks[i - 1], NULL, NULL);
+        ok(hr == S_OK, "got hr %#lx.\n", hr);
+    }
+
+    time = MFGetSystemTime();
+    test_time_source->time = 1000000 + ARRAY_SIZE(callbacks) * 200000;
+    for (i = 0; i < ARRAY_SIZE(events) && duration < 1000000; ++i)
+    {
+        DWORD w = WaitForMultipleObjects(ARRAY_SIZE(events), events, FALSE, 100);
+
+        duration = MFGetSystemTime() - time;
+
+        if (w >= WAIT_OBJECT_0 + ARRAY_SIZE(events))
+            break;
+        callback_order_end += sprintf(callback_order_end, i ? ",%lu" : "%lu", w - WAIT_OBJECT_0);
+    }
+    ok(i == ARRAY_SIZE(events), "got %u callbacks.\n", i);
+    ok(!strcmp(callback_order, "0,1,2,3,4,5,6,7,8,9"), "got callback order %s.\n", callback_order);
+
+    duration /= 10;
+    /* All overdue timers are fired at once. */
+    ok(duration < 20000, "took %lld us.\n", duration);
+
+    for (i = 0; i < ARRAY_SIZE(callbacks); ++i)
+        IMFAsyncCallback_Release(callbacks[i]);
+
+    IMFTimer_Release(timer);
     IMFPresentationTimeSource_Release(time_source);
     IMFPresentationClock_Release(clock);
 
     hr = MFShutdown();
     ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr);
+
+    timeEndPeriod(1);
 }
 
 static void test_sample_grabber(void)
diff --git a/dlls/mmdevapi/client.c b/dlls/mmdevapi/client.c
index d7e05a68215a..d4d3e82b4665 100644
--- a/dlls/mmdevapi/client.c
+++ b/dlls/mmdevapi/client.c
@@ -42,16 +42,6 @@ typedef struct tagLANGANDCODEPAGE
     WORD wCodePage;
 } LANGANDCODEPAGE;
 
-static HANDLE main_loop_thread;
-
-void main_loop_stop(void)
-{
-    if (main_loop_thread) {
-        WaitForSingleObject(main_loop_thread, INFINITE);
-        CloseHandle(main_loop_thread);
-    }
-}
-
 void set_stream_volumes(struct audio_client *This)
 {
     struct set_volumes_params params;
@@ -202,58 +192,11 @@ static void dump_fmt(const WAVEFORMATEX *fmt)
     }
 }
 
-static DWORD CALLBACK main_loop_func(void *event)
-{
-    struct main_loop_params params;
-
-    SetThreadDescription(GetCurrentThread(), L"audio_client_main");
-
-    params.event = event;
-
-    wine_unix_call(main_loop, &params);
-
-    return 0;
-}
-
-HRESULT main_loop_start(void)
-{
-    if (!main_loop_thread) {
-        HANDLE event = CreateEventW(NULL, TRUE, FALSE, NULL);
-        if (!(main_loop_thread = CreateThread(NULL, 0, main_loop_func, event, 0, NULL))) {
-            ERR("Failed to create main loop thread\n");
-            CloseHandle(event);
-            return E_FAIL;
-        }
-
-        SetThreadPriority(main_loop_thread, THREAD_PRIORITY_TIME_CRITICAL);
-        WaitForSingleObject(event, INFINITE);
-        CloseHandle(event);
-    }
-
-    return S_OK;
-}
-
-static DWORD CALLBACK timer_loop_func(void *user)
-{
-    struct timer_loop_params params;
-    struct audio_client *This = user;
-
-    SetThreadDescription(GetCurrentThread(), L"audio_client_timer");
-
-    params.stream = This->stream;
-
-    wine_unix_call(timer_loop, &params);
-
-    return 0;
-}
-
-HRESULT stream_release(stream_handle stream, HANDLE timer_thread)
+static HRESULT stream_release(stream_handle stream)
 {
     struct release_stream_params params;
 
-    params.stream       = stream;
-    params.timer_thread = timer_thread;
-
+    params.stream = stream;
     wine_unix_call(release_stream, &params);
 
     return params.result;
@@ -503,10 +446,7 @@ static HRESULT stream_init(struct audio_client *client, const BOOLEAN force_def_
         return AUDCLNT_E_ALREADY_INITIALIZED;
     }
 
-    if (FAILED(params.result = main_loop_start())) {
-        sessions_unlock();
-        return params.result;
-    }
+    wine_unix_call( main_loop_start, NULL );
 
     if (flags & AUDCLNT_STREAMFLAGS_LOOPBACK) {
         struct get_loopback_capture_device_params params;
@@ -577,7 +517,7 @@ static HRESULT stream_init(struct audio_client *client, const BOOLEAN force_def_
 
 exit:
     if (FAILED(params.result)) {
-        stream_release(stream, NULL);
+        stream_release(stream);
         free(client->vols);
         client->vols = NULL;
     } else {
@@ -765,7 +705,7 @@ static ULONG WINAPI client_Release(IAudioClient3 *iface)
         free(This->vols);
 
         if (This->stream)
-            stream_release(This->stream, This->timer_thread);
+            stream_release(This->stream);
 
         free(This->device_name);
         free(This);
@@ -979,15 +919,6 @@ static HRESULT WINAPI client_Start(IAudioClient3 *iface)
     params.stream = This->stream;
     wine_unix_call(start, &params);
 
-    if (SUCCEEDED(params.result) && !This->timer_thread) {
-        if ((This->timer_thread = CreateThread(NULL, 0, timer_loop_func, This, 0, NULL)))
-            SetThreadPriority(This->timer_thread, THREAD_PRIORITY_TIME_CRITICAL);
-        else {
-            IAudioClient3_Stop(&This->IAudioClient3_iface);
-            params.result = E_FAIL;
-        }
-    }
-
     sessions_unlock();
 
     return params.result;
diff --git a/dlls/mmdevapi/main.c b/dlls/mmdevapi/main.c
index 21b46b988f1b..70eace863e15 100644
--- a/dlls/mmdevapi/main.c
+++ b/dlls/mmdevapi/main.c
@@ -211,7 +211,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
             }
             if (lpvReserved) break;
 
-            main_loop_stop();
+            wine_unix_call( main_loop_stop, NULL );
             if (drvs.module_unixlib)
             {
                 __wine_unload_unix_lib( drvs.module );
diff --git a/dlls/mmdevapi/mmdevapi_private.h b/dlls/mmdevapi/mmdevapi_private.h
index acd3baa8cc45..b475d8195585 100644
--- a/dlls/mmdevapi/mmdevapi_private.h
+++ b/dlls/mmdevapi/mmdevapi_private.h
@@ -75,8 +75,6 @@ struct audio_client {
     UINT32 channel_count;
     stream_handle stream;
 
-    HANDLE timer_thread;
-
     struct audio_session *session;
     struct audio_session_wrapper *session_wrapper;
 
@@ -129,8 +127,6 @@ extern BOOL get_device_name_from_guid( const GUID *guid, char **name, EDataFlow
 extern HRESULT load_devices_from_reg(void);
 extern HRESULT load_driver_devices(EDataFlow flow);
 
-extern void main_loop_stop(void);
-
 extern const WCHAR drv_keyW[];
 
 extern HRESULT get_audio_session(const GUID *sessionguid, IMMDevice *device, UINT channels,
diff --git a/dlls/mmdevapi/unixlib.h b/dlls/mmdevapi/unixlib.h
index 3df13e93b398..ddb6bd1005a2 100644
--- a/dlls/mmdevapi/unixlib.h
+++ b/dlls/mmdevapi/unixlib.h
@@ -20,6 +20,25 @@
 #include "audioclient.h"
 #include "mmdeviceapi.h"
 
+#ifdef WINE_UNIX_LIB
+/* helper to create a thread on the Unix side */
+static inline NTSTATUS create_unix_thread( HANDLE *handle, const WCHAR *name,
+                                           void (*entry)(void *), void *param )
+{
+    DWORD priority = THREAD_PRIORITY_TIME_CRITICAL;
+    THREAD_NAME_INFORMATION info;
+    NTSTATUS status;
+
+    if (!(status = PsCreateSystemThread( handle, THREAD_ALL_ACCESS, NULL, 0, NULL, entry, param )))
+    {
+        RtlInitUnicodeString( &info.ThreadName, name );
+        NtSetInformationThread( *handle, ThreadNameInformation, &info, sizeof(info) );
+        NtSetInformationThread( *handle, ThreadBasePriority, &priority, sizeof(priority) );
+    }
+    return status;
+}
+#endif
+
 typedef UINT64 stream_handle;
 
 enum driver_priority
@@ -36,11 +55,6 @@ struct endpoint
     unsigned int device;
 };
 
-struct main_loop_params
-{
-    HANDLE event;
-};
-
 struct get_endpoint_ids_params
 {
     EDataFlow flow;
@@ -69,7 +83,6 @@ struct create_stream_params
 struct release_stream_params
 {
     stream_handle stream;
-    HANDLE timer_thread;
     HRESULT result;
 };
 
@@ -91,11 +104,6 @@ struct reset_params
     HRESULT result;
 };
 
-struct timer_loop_params
-{
-    stream_handle stream;
-};
-
 struct get_render_buffer_params
 {
     stream_handle stream;
@@ -315,14 +323,14 @@ enum unix_funcs
 {
     process_attach,
     process_detach,
-    main_loop,
+    main_loop_start,
+    main_loop_stop,
     get_endpoint_ids,
     create_stream,
     release_stream,
     start,
     stop,
     reset,
-    timer_loop,
     get_render_buffer,
     release_render_buffer,
     get_capture_buffer,
diff --git a/dlls/msctf/tests/Makefile.in b/dlls/msctf/tests/Makefile.in
index dc572d4d9c10..029e30de451c 100644
--- a/dlls/msctf/tests/Makefile.in
+++ b/dlls/msctf/tests/Makefile.in
@@ -1,5 +1,5 @@
 TESTDLL   = msctf.dll
-IMPORTS   = ole32 user32 advapi32
+IMPORTS   = oleaut32 ole32 user32 advapi32
 
 SOURCES = \
 	inputprocessor.c \
diff --git a/dlls/msctf/tests/inputprocessor.c b/dlls/msctf/tests/inputprocessor.c
index 5038b733b556..b912ecda697b 100644
--- a/dlls/msctf/tests/inputprocessor.c
+++ b/dlls/msctf/tests/inputprocessor.c
@@ -29,6 +29,7 @@
 #include "shlguid.h"
 #include "comcat.h"
 #include "msctf.h"
+#include "ctffunc.h"
 #include "olectl.h"
 
 static ITfInputProcessorProfiles* g_ipp;
@@ -1082,6 +1083,7 @@ DEFINE_GUID(GUID_COMPARTMENT_SPEECH_GLOBALSTATE,    0x2a54fe8e,0x0d08,0x460c,0xa
 DEFINE_GUID(GUID_COMPARTMENT_PERSISTMENUENABLED,    0x575f3783,0x70c8,0x47c8,0xae,0x5d,0x91,0xa0,0x1a,0x1f,0x75,0x92);
 DEFINE_GUID(GUID_COMPARTMENT_EMPTYCONTEXT,          0xd7487dbf,0x804e,0x41c5,0x89,0x4d,0xad,0x96,0xfd,0x4e,0xea,0x13);
 DEFINE_GUID(GUID_COMPARTMENT_TIPUISTATUS,           0x148ca3ec,0x0366,0x401c,0x8d,0x75,0xed,0x97,0x8d,0x85,0xfb,0xc9);
+DEFINE_GUID(GUID_SYSTEM_FUNCTIONPROVIDER, 0x9a698bb0,0x0f21,0x11d3,0x8d,0xf1,0x00,0x10,0x5a,0x27,0x99,0xb5);
 
 static HRESULT initialize(void)
 {
@@ -2627,6 +2629,28 @@ static void test_MultiThreadApartment(void)
     CloseHandle(thread);
 }
 
+static void test_function_provider(void)
+{
+    ITfFunctionProvider *provider;
+    ITfFnReconversion *reconv;
+    BSTR name;
+    HRESULT hr;
+
+    hr = ITfThreadMgr_GetFunctionProvider(g_tm, &GUID_SYSTEM_FUNCTIONPROVIDER, &provider);
+    ok(hr == S_OK, "got %lx\n", hr);
+
+    hr = ITfFunctionProvider_GetFunction(provider, &GUID_NULL, &IID_ITfFnReconversion, (IUnknown **)&reconv);
+    ok(hr == S_OK, "got %lx\n", hr);
+
+    hr = ITfFnReconversion_GetDisplayName(reconv, &name);
+    ok(hr == S_OK, "got %lx\n", hr);
+    ok(!wcscmp(name, L"Reconversion"), "got %s\n", debugstr_w(name));
+    SysFreeString(name);
+
+    ITfFnReconversion_Release(reconv);
+    ITfFunctionProvider_Release(provider);
+}
+
 START_TEST(inputprocessor)
 {
     if (SUCCEEDED(initialize()))
@@ -2657,6 +2681,7 @@ START_TEST(inputprocessor)
         test_Unregister();
         test_profile_mgr();
         test_MultiThreadApartment();
+        test_function_provider();
 
         ITextStoreACPSink_Release(ACPSink);
         ITfDocumentMgr_Release(g_dm);
diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c
index 11c79c030fff..312b527eb74a 100644
--- a/dlls/msctf/threadmgr.c
+++ b/dlls/msctf/threadmgr.c
@@ -34,6 +34,10 @@
 
 #include "msctf.h"
 #include "msctf_internal.h"
+#include "initguid.h"
+#include "ctffunc.h"
+
+DEFINE_GUID(GUID_SYSTEM_FUNCTIONPROVIDER, 0x9a698bb0,0x0f21,0x11d3,0x8d,0xf1,0x00,0x10,0x5a,0x27,0x99,0xb5);
 
 WINE_DEFAULT_DEBUG_CHANNEL(msctf);
 
@@ -70,6 +74,7 @@ typedef struct tagACLMulti {
     /* const ITfLangBarItemMgrVtbl *LangBarItemMgrVtbl; */
     ITfUIElementMgr ITfUIElementMgr_iface;
     ITfSourceSingle ITfSourceSingle_iface;
+    ITfFunctionProvider ITfFunctionProvider_iface;
     LONG refCount;
 
     /* Aggregation */
@@ -150,11 +155,182 @@ static inline ThreadMgr *impl_from_ITfSourceSingle(ITfSourceSingle *iface)
     return CONTAINING_RECORD(iface, ThreadMgr, ITfSourceSingle_iface);
 }
 
+static ThreadMgr *impl_from_ITfFunctionProvider(ITfFunctionProvider *iface)
+{
+    return CONTAINING_RECORD(iface, ThreadMgr, ITfFunctionProvider_iface);
+}
+
 static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMgrs *iface)
 {
     return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface);
 }
 
+struct reconv
+{
+    ITfFnReconversion ITfFnReconversion_iface;
+    LONG ref;
+};
+
+static inline struct reconv *impl_from_ITfFnReconversion(ITfFnReconversion *iface)
+{
+    return CONTAINING_RECORD(iface, struct reconv, ITfFnReconversion_iface);
+}
+
+static HRESULT WINAPI reconv_QueryInterface(ITfFnReconversion *iface, REFIID iid, void **out)
+{
+    TRACE("(%p) %s, %p.\n", iface, debugstr_guid(iid), out);
+
+    *out = NULL;
+    if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfFunction)
+        || IsEqualIID(iid, &IID_ITfFnReconversion))
+    {
+        *out = iface;
+        ITfFnReconversion_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("unsupported interface: %s\n", debugstr_guid(iid));
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI reconv_AddRef(ITfFnReconversion *iface)
+{
+    struct reconv *reconv = impl_from_ITfFnReconversion(iface);
+    return InterlockedIncrement(&reconv->ref);
+}
+
+static ULONG WINAPI reconv_Release(ITfFnReconversion *iface)
+{
+    struct reconv *reconv = impl_from_ITfFnReconversion(iface);
+    ULONG ret;
+
+    if (!(ret = InterlockedDecrement(&reconv->ref))) free(reconv);
+    return ret;
+}
+
+static HRESULT WINAPI reconv_GetDisplayName(ITfFnReconversion *iface, BSTR *name)
+{
+    BSTR str;
+
+    TRACE("(%p) %p\n", iface, name);
+
+    if (!(str = SysAllocString(L"Reconversion"))) return E_OUTOFMEMORY;
+    *name = str;
+    return S_OK;
+}
+
+static HRESULT WINAPI reconv_QueryRange(ITfFnReconversion *iface, ITfRange *range, ITfRange **new_range,
+                                        BOOL *convertable)
+{
+    FIXME("(%p) %p %p %p stub.\n", iface, range, new_range, convertable);
+
+    *convertable = FALSE;
+    *new_range = NULL;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI reconv_GetReconversion(ITfFnReconversion *iface, ITfRange *range, ITfCandidateList **cand_list)
+{
+    FIXME("(%p) %p %p stub.\n", iface, range, cand_list);
+
+    *cand_list = NULL;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI reconv_Reconvert(ITfFnReconversion *iface, ITfRange *range)
+{
+    FIXME("(%p) %p stub.\n", iface, range);
+    return E_NOTIMPL;
+}
+
+static ITfFnReconversionVtbl reconv_vtbl =
+{
+    reconv_QueryInterface,
+    reconv_AddRef,
+    reconv_Release,
+    reconv_GetDisplayName,
+    reconv_QueryRange,
+    reconv_GetReconversion,
+    reconv_Reconvert,
+};
+
+static HRESULT ITfFnReconversion_Constructor(IUnknown **out)
+{
+    struct reconv *reconv;
+
+    if (!(reconv = calloc(1, sizeof(*reconv)))) return E_OUTOFMEMORY;
+
+    reconv->ITfFnReconversion_iface.lpVtbl= &reconv_vtbl;
+    reconv->ref = 1;
+
+    TRACE("returning %p\n", &reconv->ITfFnReconversion_iface);
+    *out = (IUnknown *)&reconv->ITfFnReconversion_iface;
+    return S_OK;
+}
+
+static HRESULT WINAPI func_provider_QueryInterface(ITfFunctionProvider *iface, REFIID iid, LPVOID *ppvOut)
+{
+    ThreadMgr *This = impl_from_ITfFunctionProvider(iface);
+    return ITfThreadMgrEx_QueryInterface(&This->ITfThreadMgrEx_iface, iid, ppvOut);
+}
+
+static ULONG WINAPI func_provider_AddRef(ITfFunctionProvider *iface)
+{
+    ThreadMgr *This = impl_from_ITfFunctionProvider(iface);
+    return ITfThreadMgrEx_AddRef(&This->ITfThreadMgrEx_iface);
+}
+
+static ULONG WINAPI func_provider_Release(ITfFunctionProvider *iface)
+{
+    ThreadMgr *This = impl_from_ITfFunctionProvider(iface);
+    return ITfThreadMgrEx_Release(&This->ITfThreadMgrEx_iface);
+}
+
+static HRESULT WINAPI func_provider_GetType(ITfFunctionProvider *iface, GUID *guid)
+{
+    FIXME("(%p) %p stub.\n", iface, guid);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI func_provider_GetDescription(ITfFunctionProvider *iface, BSTR *desc)
+{
+    FIXME("(%p) %p stub.\n", iface, desc);
+    return E_NOTIMPL;
+}
+
+static const struct
+{
+    const GUID *iid;
+    HRESULT (*constructor)(IUnknown **);
+}
+function_table[] =
+{
+    { &IID_ITfFnReconversion, ITfFnReconversion_Constructor },
+};
+
+static HRESULT WINAPI func_provider_GetFunction(ITfFunctionProvider *iface, REFGUID guid, REFIID riid, IUnknown **func)
+{
+    ULONG i;
+
+    TRACE("(%p) %s %s %p\n", iface, debugstr_guid(guid), debugstr_guid(riid), func);
+
+    for (i = 0; i < ARRAY_SIZE(function_table); i++)
+        if (IsEqualIID(riid, function_table[i].iid)) return function_table[i].constructor(func);
+
+    FIXME("(%p) %s %s %p stub.\n", iface, debugstr_guid(guid), debugstr_guid(riid), func);
+    return E_NOTIMPL;
+}
+
+static const ITfFunctionProviderVtbl TfFunctionProviderVtbl =
+{
+    func_provider_QueryInterface,
+    func_provider_AddRef,
+    func_provider_Release,
+    func_provider_GetType,
+    func_provider_GetDescription,
+    func_provider_GetFunction,
+};
+
 static void ThreadMgr_Destructor(ThreadMgr *This)
 {
     struct list *cursor, *cursor2;
@@ -493,8 +669,18 @@ static HRESULT WINAPI ThreadMgr_GetFunctionProvider(ITfThreadMgrEx *iface, REFCL
 ITfFunctionProvider **ppFuncProv)
 {
     ThreadMgr *This = impl_from_ITfThreadMgrEx(iface);
-    FIXME("STUB:(%p)\n",This);
-    return E_NOTIMPL;
+
+    TRACE("(%p) %s\n", This, debugstr_guid(clsid));
+
+    if (!IsEqualGUID(clsid, &GUID_SYSTEM_FUNCTIONPROVIDER))
+    {
+        FIXME("clsid %s not supported\n", debugstr_guid(clsid));
+        return E_NOTIMPL;
+    }
+
+    *ppFuncProv = &This->ITfFunctionProvider_iface;
+    ITfFunctionProvider_AddRef(*ppFuncProv);
+    return S_OK;
 }
 
 static HRESULT WINAPI ThreadMgr_EnumFunctionProviders(ITfThreadMgrEx *iface,
@@ -1366,6 +1552,7 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut)
     This->ITfThreadMgrEventSink_iface.lpVtbl = &ThreadMgrEventSinkVtbl;
     This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl;
     This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl;
+    This->ITfFunctionProvider_iface.lpVtbl = &TfFunctionProviderVtbl;
     This->refCount = 1;
     TlsSetValue(tlsIndex,This);
 
diff --git a/dlls/msvcp100/msvcp100.spec b/dlls/msvcp100/msvcp100.spec
index 08e4054aabdd..2d1ae1e7b706 100644
--- a/dlls/msvcp100/msvcp100.spec
+++ b/dlls/msvcp100/msvcp100.spec
@@ -3837,14 +3837,14 @@
 @ cdecl -ret64 -arch=arm ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAA_JPB_W_J@Z(ptr wstr int64) basic_streambuf_wchar_xsputn
 @ thiscall -ret64 -arch=i386 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE_JPB_W_J@Z(ptr wstr int64) basic_streambuf_wchar_xsputn
 @ cdecl -arch=win64 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MEAA_JPEB_W_J@Z(ptr wstr int64) basic_streambuf_wchar_xsputn
-@ stub _Cosh
+@ cdecl _Cosh(double double)
 @ extern _Denorm
 @ stub _Dnorm
 @ cdecl _Dscale(ptr long)
 @ cdecl _Dtest(ptr)
 @ extern _Eps
 @ cdecl _Exp(ptr double long)
-@ stub _FCosh
+@ cdecl _FCosh(float float)
 @ extern _FDenorm
 @ stub _FDnorm
 @ cdecl _FDscale(ptr long)
@@ -3854,7 +3854,7 @@
 @ extern _FInf
 @ extern _FNan
 # extern _FRteps
-@ stub _FSinh
+@ cdecl _FSinh(float float)
 @ extern _FSnan
 # extern _FXbig
 @ stub _GetLocaleForCP
@@ -3866,7 +3866,7 @@
 @ cdecl _Getwctypes(ptr ptr ptr ptr)
 @ extern _Hugeval
 @ extern _Inf
-@ stub _LCosh
+@ cdecl _LCosh(double double) _Cosh
 @ extern _LDenorm
 @ cdecl _LDscale(ptr long) _Dscale
 @ cdecl _LDtest(ptr) _Dtest
@@ -3876,7 +3876,7 @@
 @ extern _LNan
 @ stub _LPoly
 # extern _LRteps
-@ stub _LSinh
+@ cdecl _LSinh(double double) _Sinh
 @ extern _LSnan
 # extern _LXbig
 @ extern _LZero
@@ -3889,7 +3889,7 @@
 @ stub _Once
 @ stub _Poly
 # extern _Rteps
-@ stub _Sinh
+@ cdecl _Sinh(double double)
 @ extern _Snan
 @ cdecl _Stod(ptr ptr long)
 @ cdecl _Stodx(ptr ptr long ptr)
diff --git a/dlls/msvcp110/msvcp110.spec b/dlls/msvcp110/msvcp110.spec
index 2bc7e0bbeecd..f3a21952ac6d 100644
--- a/dlls/msvcp110/msvcp110.spec
+++ b/dlls/msvcp110/msvcp110.spec
@@ -3738,7 +3738,7 @@
 @ cdecl _Cnd_timedwait(ptr ptr ptr)
 @ cdecl _Cnd_unregister_at_thread_exit(ptr)
 @ cdecl _Cnd_wait(ptr ptr)
-@ stub _Cosh
+@ cdecl _Cosh(double double)
 @ extern _Denorm
 @ stub _Dint
 @ stub _Dnorm
@@ -3749,7 +3749,7 @@
 @ stub _Dunscale
 @ extern _Eps
 @ cdecl _Exp(ptr double long)
-@ stub _FCosh
+@ cdecl _FCosh(float float)
 @ extern _FDenorm
 @ stub _FDint
 @ stub _FDnorm
@@ -3762,7 +3762,7 @@
 @ extern _FInf
 @ extern _FNan
 # extern _FRteps
-@ stub _FSinh
+@ cdecl _FSinh(float float)
 @ extern _FSnan
 # extern _FXbig
 @ stub _FXp_addh
@@ -3786,7 +3786,7 @@
 @ cdecl _Getwctypes(ptr ptr ptr ptr)
 @ extern _Hugeval
 @ extern _Inf
-@ stub _LCosh
+@ cdecl _LCosh(double double) _Cosh
 @ extern _LDenorm
 @ stub _LDint
 @ cdecl _LDscale(ptr long) _Dscale
@@ -3799,7 +3799,7 @@
 @ extern _LNan
 @ stub _LPoly
 # extern _LRteps
-@ stub _LSinh
+@ cdecl _LSinh(double double) _Sinh
 @ extern _LSnan
 # extern _LXbig
 @ stub _LXp_addh
@@ -3835,7 +3835,7 @@
 @ stub _Once
 @ stub _Poly
 # extern _Rteps
-@ stub _Sinh
+@ cdecl _Sinh(double double)
 @ extern _Snan
 @ cdecl _Stod(ptr ptr long)
 @ cdecl _Stodx(ptr ptr long ptr)
diff --git a/dlls/msvcp120/msvcp120.spec b/dlls/msvcp120/msvcp120.spec
index 0a9f3ca47ee7..88636418a812 100644
--- a/dlls/msvcp120/msvcp120.spec
+++ b/dlls/msvcp120/msvcp120.spec
@@ -3679,7 +3679,7 @@
 @ cdecl _Cnd_timedwait(ptr ptr ptr)
 @ cdecl _Cnd_unregister_at_thread_exit(ptr)
 @ cdecl _Cnd_wait(ptr ptr)
-@ stub _Cosh
+@ cdecl _Cosh(double double)
 @ extern _Denorm
 @ stub _Dint
 @ stub _Dnorm
@@ -3690,7 +3690,7 @@
 @ stub _Dunscale
 @ extern _Eps
 @ cdecl _Exp(ptr double long)
-@ stub _FCosh
+@ cdecl _FCosh(float float)
 @ extern _FDenorm
 @ stub _FDint
 @ stub _FDnorm
@@ -3705,7 +3705,7 @@
 @ stub _FPlsw
 @ stub _FPmsw
 # extern _FRteps
-@ stub _FSinh
+@ cdecl _FSinh(float float)
 @ extern _FSnan
 # extern _FXbig
 @ stub _FXp_addh
@@ -3729,7 +3729,7 @@
 @ cdecl _Getwctypes(ptr ptr ptr ptr)
 @ extern _Hugeval
 @ extern _Inf
-@ stub _LCosh
+@ cdecl _LCosh(double double) _Cosh
 @ extern _LDenorm
 @ stub _LDint
 @ cdecl _LDscale(ptr long) _Dscale
@@ -3744,7 +3744,7 @@
 @ stub _LPmsw
 @ stub _LPoly
 # extern _LRteps
-@ stub _LSinh
+@ cdecl _LSinh(double double) _Sinh
 @ extern _LSnan
 # extern _LXbig
 @ stub _LXp_addh
@@ -3782,7 +3782,7 @@
 @ stub _Pmsw
 @ stub _Poly
 # extern _Rteps
-@ stub _Sinh
+@ cdecl _Sinh(double double)
 @ extern _Snan
 @ cdecl _Stod(ptr ptr long)
 @ cdecl _Stodx(ptr ptr long ptr)
diff --git a/dlls/msvcp120_app/msvcp120_app.spec b/dlls/msvcp120_app/msvcp120_app.spec
index 75651fabd1be..081de4a492b1 100644
--- a/dlls/msvcp120_app/msvcp120_app.spec
+++ b/dlls/msvcp120_app/msvcp120_app.spec
@@ -3679,7 +3679,7 @@
 @ cdecl _Cnd_timedwait(ptr ptr ptr) msvcp120._Cnd_timedwait
 @ cdecl _Cnd_unregister_at_thread_exit(ptr) msvcp120._Cnd_unregister_at_thread_exit
 @ cdecl _Cnd_wait(ptr ptr) msvcp120._Cnd_wait
-@ stub _Cosh
+@ cdecl _Cosh(double double) msvcp120._Cosh
 @ extern _Denorm msvcp120._Denorm
 @ stub _Dint
 @ stub _Dnorm
@@ -3690,7 +3690,7 @@
 @ stub _Dunscale
 @ extern _Eps msvcp120._Eps
 @ cdecl _Exp(ptr double long) msvcp120._Exp
-@ stub _FCosh
+@ cdecl _FCosh(float float) msvcp120._FCosh
 @ extern _FDenorm msvcp120._FDenorm
 @ stub _FDint
 @ stub _FDnorm
@@ -3705,7 +3705,7 @@
 @ stub _FPlsw
 @ stub _FPmsw
 # extern _FRteps
-@ stub _FSinh
+@ cdecl _FSinh(float float) msvcp120._FSinh
 @ extern _FSnan msvcp120._FSnan
 # extern _FXbig
 @ stub _FXp_addh
@@ -3729,7 +3729,7 @@
 @ cdecl _Getwctypes(ptr ptr ptr ptr) msvcp120._Getwctypes
 @ extern _Hugeval msvcp120._Hugeval
 @ extern _Inf msvcp120._Inf
-@ stub _LCosh
+@ cdecl _LCosh(double double) msvcp120._LCosh
 @ extern _LDenorm msvcp120._LDenorm
 @ stub _LDint
 @ cdecl _LDscale(ptr long) msvcp120._LDscale
@@ -3744,7 +3744,7 @@
 @ stub _LPmsw
 @ stub _LPoly
 # extern _LRteps
-@ stub _LSinh
+@ cdecl _LSinh(double double) msvcp120._LSinh
 @ extern _LSnan msvcp120._LSnan
 # extern _LXbig
 @ stub _LXp_addh
@@ -3782,7 +3782,7 @@
 @ stub _Pmsw
 @ stub _Poly
 # extern _Rteps
-@ stub _Sinh
+@ cdecl _Sinh(double double) msvcp120._Sinh
 @ extern _Snan msvcp120._Snan
 @ cdecl _Stod(ptr ptr long) msvcp120._Stod
 @ cdecl _Stodx(ptr ptr long ptr) msvcp120._Stodx
diff --git a/dlls/msvcp140/msvcp140.spec b/dlls/msvcp140/msvcp140.spec
index 4faf71ae3f77..cd6c89e22e7b 100644
--- a/dlls/msvcp140/msvcp140.spec
+++ b/dlls/msvcp140/msvcp140.spec
@@ -3634,20 +3634,20 @@
 @ cdecl _Cnd_unregister_at_thread_exit(ptr)
 @ cdecl _Cnd_wait(ptr ptr)
 @ cdecl _Copy_file(wstr wstr)
-@ stub _Cosh
+@ cdecl _Cosh(double double)
 @ cdecl _Current_get(ptr)
 @ cdecl _Current_set(wstr) tr2_sys__Current_set_wchar
 @ extern _Denorm
 @ cdecl _Dtest(ptr)
 @ cdecl _Equivalent(wstr wstr) _Equivalent
 @ cdecl _Exp(ptr double long)
-@ stub _FCosh
+@ cdecl _FCosh(float float)
 @ extern _FDenorm
 @ cdecl _FDtest(ptr)
 @ cdecl _FExp(ptr float long)
 @ extern _FInf
 @ extern _FNan
-@ stub _FSinh
+@ cdecl _FSinh(float float)
 @ extern _FSnan
 @ cdecl _File_size(wstr)
 @ cdecl -norelay _Getcoll()
@@ -3659,13 +3659,13 @@
 @ stub _Hard_links
 @ extern _Hugeval
 @ extern _Inf
-@ stub _LCosh
+@ cdecl _LCosh(double double) _Cosh
 @ extern _LDenorm
 @ cdecl _LDtest(ptr) _Dtest
 @ stub _LExp
 @ extern _LInf
 @ extern _LNan
-@ stub _LSinh
+@ cdecl _LSinh(double double) _Sinh
 @ extern _LSnan
 @ cdecl _Last_write_time(int64)
 @ cdecl _Link(wstr wstr) tr2_sys__Link_wchar
@@ -3698,7 +3698,7 @@
 @ cdecl _Rename(wstr wstr) tr2_sys__Rename_wchar
 @ cdecl _Resize(wstr int64)
 @ cdecl _Set_last_write_time(wstr int64)
-@ stub _Sinh
+@ cdecl _Sinh(double double)
 @ extern _Snan
 @ cdecl _Stat(wstr ptr)
 @ stub _Statvfs
diff --git a/dlls/msvcp60/msvcp60.spec b/dlls/msvcp60/msvcp60.spec
index fabb9592be60..bdffa3fdf257 100644
--- a/dlls/msvcp60/msvcp60.spec
+++ b/dlls/msvcp60/msvcp60.spec
@@ -2574,9 +2574,9 @@
 @ cdecl -arch=arm ?_Copy@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@AAAXI@Z(ptr long) basic_string_wchar__Copy
 @ thiscall -arch=i386 ?_Copy@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@AAEXI@Z(ptr long) basic_string_wchar__Copy
 @ cdecl -arch=win64 ?_Copy@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@AEAAX_K@Z(ptr long) basic_string_wchar__Copy
-@ stub ?_Cosh@?$_Ctr@M@std@@SAMMM@Z
-@ stub ?_Cosh@?$_Ctr@N@std@@SANNN@Z
-@ stub ?_Cosh@?$_Ctr@O@std@@SAOOO@Z
+@ cdecl ?_Cosh@?$_Ctr@M@std@@SAMMM@Z(float float) _FCosh
+@ cdecl ?_Cosh@?$_Ctr@N@std@@SANNN@Z(double double) _Cosh
+@ cdecl ?_Cosh@?$_Ctr@O@std@@SAOOO@Z(double double) _Cosh
 @ cdecl -arch=arm ?_Decref@facet@locale@std@@QAAPAV123@XZ(ptr) locale_facet__Decref
 @ thiscall -arch=i386 ?_Decref@facet@locale@std@@QAEPAV123@XZ(ptr) locale_facet__Decref
 @ cdecl -arch=win64 ?_Decref@facet@locale@std@@QEAAPEAV123@XZ(ptr) locale_facet__Decref
@@ -3003,9 +3003,9 @@
 @ stub -arch=win64 ?_Rep@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@KA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@D_K@Z
 @ stub -arch=win32 ?_Rep@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@KA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@GI@Z
 @ stub -arch=win64 ?_Rep@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@KA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@G_K@Z
-@ stub ?_Sinh@?$_Ctr@M@std@@SAMMM@Z
-@ stub ?_Sinh@?$_Ctr@N@std@@SANNN@Z
-@ stub ?_Sinh@?$_Ctr@O@std@@SAOOO@Z
+@ cdecl ?_Sinh@?$_Ctr@M@std@@SAMMM@Z(float float) _FSinh
+@ cdecl ?_Sinh@?$_Ctr@N@std@@SANNN@Z(double double) _Sinh
+@ cdecl ?_Sinh@?$_Ctr@O@std@@SAOOO@Z(double double) _Sinh
 @ cdecl -arch=arm ?_Split@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AAAXXZ(ptr) basic_string_char__Split
 @ thiscall -arch=i386 ?_Split@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AAEXXZ(ptr) basic_string_char__Split
 @ cdecl -arch=win64 ?_Split@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEAAXXZ(ptr) basic_string_char__Split
@@ -5633,14 +5633,14 @@
 @ cdecl -arch=arm ?xsputn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAAHPBGH@Z(ptr ptr long) basic_streambuf_wchar_xsputn
 @ thiscall -arch=i386 ?xsputn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEHPBGH@Z(ptr ptr long) basic_streambuf_wchar_xsputn
 @ cdecl -arch=win64 ?xsputn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MEAA_JPEBG_J@Z(ptr ptr long) basic_streambuf_wchar_xsputn
-@ stub _Cosh
+@ cdecl _Cosh(double double)
 @ extern _Denorm _Denorm
 @ stub _Dnorm
 @ cdecl _Dscale(ptr long)
 @ cdecl _Dtest(ptr)
 @ extern _Eps _Eps
 @ cdecl _Exp(ptr double long)
-@ stub _FCosh
+@ cdecl _FCosh(float float)
 @ extern _FDenorm _FDenorm
 @ stub _FDnorm
 @ cdecl _FDscale(ptr long)
@@ -5650,7 +5650,7 @@
 @ extern _FInf _FInf
 @ extern _FNan _FNan
 # extern _FRteps
-@ stub _FSinh
+@ cdecl _FSinh(float float)
 @ extern _FSnan _FSnan
 # extern _FXbig
 @ cdecl -norelay _Getcoll()
@@ -5658,7 +5658,7 @@
 @ cdecl -norelay _Getcvt()
 @ extern _Hugeval _Hugeval
 @ extern _Inf _Inf
-@ stub _LCosh
+@ cdecl _LCosh(double double) _Cosh
 @ extern _LDenorm _LDenorm
 @ cdecl _LDscale(ptr long) _Dscale
 @ cdecl _LDtest(ptr) _Dtest
@@ -5668,14 +5668,14 @@
 @ extern _LNan _LNan
 @ stub _LPoly
 # extern _LRteps
-@ stub _LSinh
+@ cdecl _LSinh(double double) _Sinh
 @ extern _LSnan _LSnan
 # extern _LXbig
 @ cdecl _Mbrtowc(ptr ptr long ptr ptr)
 @ extern _Nan _Nan
 @ stub _Poly
 # extern _Rteps
-@ stub _Sinh
+@ cdecl _Sinh(double double)
 @ extern _Snan _Snan
 @ cdecl _Stod(ptr ptr long)
 @ cdecl _Stof(ptr ptr long)
diff --git a/dlls/msvcp70/msvcp70.spec b/dlls/msvcp70/msvcp70.spec
index 054755c40a51..0ba16f5bd03b 100644
--- a/dlls/msvcp70/msvcp70.spec
+++ b/dlls/msvcp70/msvcp70.spec
@@ -2399,9 +2399,9 @@
 @ stub -arch=i386 ?_Copy@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@AAEXII@Z
 @ stub -arch=arm ?_Copy@?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@AAAXII@Z
 @ stub -arch=i386 ?_Copy@?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@AAEXII@Z
-@ stub ?_Cosh@?$_Ctraits@M@std@@SAMMM@Z
-@ stub ?_Cosh@?$_Ctraits@N@std@@SANNN@Z
-@ stub ?_Cosh@?$_Ctraits@O@std@@SAOOO@Z
+@ cdecl ?_Cosh@?$_Ctraits@M@std@@SAMMM@Z(float float) _FCosh
+@ cdecl ?_Cosh@?$_Ctraits@N@std@@SANNN@Z(double double) _Cosh
+@ cdecl ?_Cosh@?$_Ctraits@O@std@@SAOOO@Z(double double) _Cosh
 @ cdecl -arch=arm ?_Decref@facet@locale@std@@QAAPAV123@XZ(ptr) locale_facet__Decref
 @ thiscall -arch=i386 ?_Decref@facet@locale@std@@QAEPAV123@XZ(ptr) locale_facet__Decref
 @ cdecl -arch=win64 ?_Decref@facet@locale@std@@QEAAPEAV123@XZ(ptr) locale_facet__Decref
@@ -2951,9 +2951,9 @@
 # extern -arch=win32 ?_Root2@?CA@??_Fabs@std@@YAMABV?$complex@M@2@PAH@Z@4MB
 # extern -arch=win32 ?_Root2@?CA@??_Fabs@std@@YANABV?$complex@N@2@PAH@Z@4NB
 # extern -arch=win32 ?_Root2@?CA@??_Fabs@std@@YAOABV?$complex@O@2@PAH@Z@4OB
-@ stub ?_Sinh@?$_Ctraits@M@std@@SAMMM@Z
-@ stub ?_Sinh@?$_Ctraits@N@std@@SANNN@Z
-@ stub ?_Sinh@?$_Ctraits@O@std@@SAOOO@Z
+@ cdecl ?_Sinh@?$_Ctraits@M@std@@SAMMM@Z(float float) _FSinh
+@ cdecl ?_Sinh@?$_Ctraits@N@std@@SANNN@Z(double double) _Sinh
+@ cdecl ?_Sinh@?$_Ctraits@O@std@@SAOOO@Z(double double) _Sinh
 @ extern -arch=arm ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IAAXPAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
 @ extern -arch=i386 ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IAEXPAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
 @ extern -arch=win64 ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IEAAXPEAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
@@ -6916,14 +6916,14 @@
 @ thiscall -arch=i386 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEHPB_WH@Z(ptr ptr long) basic_streambuf_wchar_xsputn
 @ cdecl -arch=win64 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MEAA_JPEB_W_J@Z(ptr ptr long) basic_streambuf_wchar_xsputn
 @ stub _Atexit
-@ stub _Cosh
+@ cdecl _Cosh(double double)
 @ extern _Denorm
 @ stub _Dnorm
 @ cdecl _Dscale(ptr long)
 @ cdecl _Dtest(ptr)
 @ extern _Eps
 @ cdecl _Exp(ptr double long)
-@ stub _FCosh
+@ cdecl _FCosh(float float)
 @ extern _FDenorm
 @ stub _FDnorm
 @ cdecl _FDscale(ptr long)
@@ -6933,7 +6933,7 @@
 @ extern _FInf
 @ extern _FNan
 # extern _FRteps
-@ stub _FSinh
+@ cdecl _FSinh(float float)
 @ extern _FSnan
 # extern _FXbig
 # extern _FZero
@@ -6944,7 +6944,7 @@
 @ cdecl _Getwctypes(ptr ptr ptr ptr)
 @ extern _Hugeval
 @ extern _Inf
-@ stub _LCosh
+@ cdecl _LCosh(double double) _Cosh
 @ extern _LDenorm
 @ cdecl _LDscale(ptr long) _Dscale
 @ cdecl _LDtest(ptr) _Dtest
@@ -6954,7 +6954,7 @@
 @ extern _LNan
 @ stub _LPoly
 # extern _LRteps
-@ stub _LSinh
+@ cdecl _LSinh(double double) _Sinh
 @ extern _LSnan
 # extern _LXbig
 @ extern _LZero
@@ -6962,7 +6962,7 @@
 @ extern _Nan
 @ stub _Poly
 # extern _Rteps
-@ stub _Sinh
+@ cdecl _Sinh(double double)
 @ extern _Snan
 @ cdecl _Stod(ptr ptr long)
 @ cdecl _Stof(ptr ptr long)
diff --git a/dlls/msvcp71/msvcp71.spec b/dlls/msvcp71/msvcp71.spec
index 97fa0bef1603..809ec0ea09b0 100644
--- a/dlls/msvcp71/msvcp71.spec
+++ b/dlls/msvcp71/msvcp71.spec
@@ -2621,9 +2621,9 @@
 @ cdecl -arch=arm ?_Copy@?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@IAAXII@Z(ptr long long) basic_string_wchar__Copy
 @ thiscall -arch=i386 ?_Copy@?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@IAEXII@Z(ptr long long) basic_string_wchar__Copy
 @ cdecl -arch=win64 ?_Copy@?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@IEAAX_K0@Z(ptr long long) basic_string_wchar__Copy
-@ stub ?_Cosh@?$_Ctraits@M@std@@SAMMM@Z
-@ stub ?_Cosh@?$_Ctraits@N@std@@SANNN@Z
-@ stub ?_Cosh@?$_Ctraits@O@std@@SAOOO@Z
+@ cdecl ?_Cosh@?$_Ctraits@M@std@@SAMMM@Z(float float) _FCosh
+@ cdecl ?_Cosh@?$_Ctraits@N@std@@SANNN@Z(double double) _Cosh
+@ cdecl ?_Cosh@?$_Ctraits@O@std@@SAOOO@Z(double double) _Cosh
 @ cdecl -arch=arm ?_Decref@facet@locale@std@@QAAPAV123@XZ(ptr) locale_facet__Decref
 @ thiscall -arch=i386 ?_Decref@facet@locale@std@@QAEPAV123@XZ(ptr) locale_facet__Decref
 @ cdecl -arch=win64 ?_Decref@facet@locale@std@@QEAAPEAV123@XZ(ptr) locale_facet__Decref
@@ -3202,9 +3202,9 @@
 # extern -arch=win64 ?_Root2@?CA@???$_Fabs@M@std@@YAMAEBV?$complex@M@1@PEAH@Z@4MB
 # extern -arch=win32 ?_Root2@?CA@???$_Fabs@N@std@@YANABV?$complex@N@1@PAH@Z@4NB
 # extern -arch=win64 ?_Root2@?CA@???$_Fabs@N@std@@YANAEBV?$complex@N@1@PEAH@Z@4NB
-@ stub ?_Sinh@?$_Ctraits@M@std@@SAMMM@Z
-@ stub ?_Sinh@?$_Ctraits@N@std@@SANNN@Z
-@ stub ?_Sinh@?$_Ctraits@O@std@@SAOOO@Z
+@ cdecl ?_Sinh@?$_Ctraits@M@std@@SAMMM@Z(float float) _FSinh
+@ cdecl ?_Sinh@?$_Ctraits@N@std@@SANNN@Z(double double) _Sinh
+@ cdecl ?_Sinh@?$_Ctraits@O@std@@SAOOO@Z(double double) _Sinh
 @ extern -arch=arm ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IAAXPAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
 @ extern -arch=i386 ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IAEXPAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
 @ extern -arch=win64 ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IEAAXPEAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
@@ -7037,14 +7037,14 @@
 @ thiscall -arch=i386 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEHPB_WH@Z(ptr ptr long) basic_streambuf_wchar_xsputn
 @ cdecl -arch=win64 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MEAA_JPEB_W_J@Z(ptr ptr long) basic_streambuf_wchar_xsputn
 @ stub _Atexit
-@ stub _Cosh
+@ cdecl _Cosh(double double)
 @ extern _Denorm
 @ stub _Dnorm
 @ cdecl _Dscale(ptr long)
 @ cdecl _Dtest(ptr)
 @ extern _Eps
 @ cdecl _Exp(ptr double long)
-@ stub _FCosh
+@ cdecl _FCosh(float float)
 @ extern _FDenorm
 @ stub _FDnorm
 @ cdecl _FDscale(ptr long)
@@ -7054,7 +7054,7 @@
 @ extern _FInf
 @ extern _FNan
 # extern _FRteps
-@ stub _FSinh
+@ cdecl _FSinh(float float)
 @ extern _FSnan
 # extern _FXbig
 # extern _FZero
@@ -7065,7 +7065,7 @@
 @ cdecl _Getwctypes(ptr ptr ptr ptr)
 @ extern _Hugeval
 @ extern _Inf
-@ stub _LCosh
+@ cdecl _LCosh(double double) _Cosh
 @ extern _LDenorm
 @ cdecl _LDscale(ptr long) _Dscale
 @ cdecl _LDtest(ptr) _Dtest
@@ -7075,7 +7075,7 @@
 @ extern _LNan
 @ stub _LPoly
 # extern _LRteps
-@ stub _LSinh
+@ cdecl _LSinh(double double) _Sinh
 @ extern _LSnan
 # extern _LXbig
 @ extern _LZero
@@ -7083,7 +7083,7 @@
 @ extern _Nan
 @ stub _Poly
 # extern _Rteps
-@ stub _Sinh
+@ cdecl _Sinh(double double)
 @ extern _Snan
 @ cdecl _Stod(ptr ptr long)
 @ cdecl _Stof(ptr ptr long)
diff --git a/dlls/msvcp80/msvcp80.spec b/dlls/msvcp80/msvcp80.spec
index 9b923fd9d50d..f653bc066647 100644
--- a/dlls/msvcp80/msvcp80.spec
+++ b/dlls/msvcp80/msvcp80.spec
@@ -2898,9 +2898,9 @@
 @ cdecl -arch=win64 ?_Copy_s@?$char_traits@G@std@@SAPEAGPEAG_KPEBG1@Z(ptr long ptr long) MSVCP_char_traits_short__Copy_s
 @ cdecl -arch=win32 ?_Copy_s@?$char_traits@_W@std@@SAPA_WPA_WIPB_WI@Z(ptr long ptr long) MSVCP_char_traits_wchar__Copy_s
 @ cdecl -arch=win64 ?_Copy_s@?$char_traits@_W@std@@SAPEA_WPEA_W_KPEB_W1@Z(ptr long ptr long) MSVCP_char_traits_wchar__Copy_s
-@ stub ?_Cosh@?$_Ctraits@M@std@@SAMMM@Z
-@ stub ?_Cosh@?$_Ctraits@N@std@@SANNN@Z
-@ stub ?_Cosh@?$_Ctraits@O@std@@SAOOO@Z
+@ cdecl ?_Cosh@?$_Ctraits@M@std@@SAMMM@Z(float float) _FCosh
+@ cdecl ?_Cosh@?$_Ctraits@N@std@@SANNN@Z(double double) _Cosh
+@ cdecl ?_Cosh@?$_Ctraits@O@std@@SAOOO@Z(double double) _Cosh
 @ cdecl -arch=win32 ?_Cout_func@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@XZ() _Cout_func
 @ cdecl -arch=win64 ?_Cout_func@std@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@1@XZ() _Cout_func
 @ stub -arch=win32 ?_Debug_message@std@@YAXPBD0@Z
@@ -3662,9 +3662,9 @@
 @ cdecl -arch=arm ?_Sgetn_s@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAAHPA_WIH@Z(ptr ptr long long) basic_streambuf_wchar__Sgetn_s
 @ thiscall -arch=i386 ?_Sgetn_s@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEHPA_WIH@Z(ptr ptr long long) basic_streambuf_wchar__Sgetn_s
 @ cdecl -arch=win64 ?_Sgetn_s@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QEAA_JPEA_W_K_J@Z(ptr ptr long long) basic_streambuf_wchar__Sgetn_s
-@ stub ?_Sinh@?$_Ctraits@M@std@@SAMMM@Z
-@ stub ?_Sinh@?$_Ctraits@N@std@@SANNN@Z
-@ stub ?_Sinh@?$_Ctraits@O@std@@SAOOO@Z
+@ cdecl ?_Sinh@?$_Ctraits@M@std@@SAMMM@Z(float float) _FSinh
+@ cdecl ?_Sinh@?$_Ctraits@N@std@@SANNN@Z(double double) _Sinh
+@ cdecl ?_Sinh@?$_Ctraits@O@std@@SAOOO@Z(double double) _Sinh
 @ extern -arch=arm ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IAAXPAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
 @ extern -arch=i386 ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IAEXPAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
 @ extern -arch=win64 ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IEAAXPEAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
@@ -7829,7 +7829,7 @@
 @ cdecl -arch=arm ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAAHPB_WH@Z(ptr ptr long) basic_streambuf_wchar_xsputn
 @ thiscall -arch=i386 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEHPB_WH@Z(ptr ptr long) basic_streambuf_wchar_xsputn
 @ cdecl -arch=win64 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MEAA_JPEB_W_J@Z(ptr ptr long) basic_streambuf_wchar_xsputn
-@ stub _Cosh
+@ cdecl _Cosh(double double)
 @ extern _Denorm
 @ stub _Dnorm
 @ cdecl _Dscale(ptr long)
@@ -7838,7 +7838,7 @@
 @ stub _Dunscale
 @ extern _Eps
 @ cdecl _Exp(ptr double long)
-@ stub _FCosh
+@ cdecl _FCosh(float float)
 @ extern _FDenorm
 @ stub _FDnorm
 @ cdecl _FDscale(ptr long)
@@ -7850,7 +7850,7 @@
 @ extern _FInf
 @ extern _FNan
 # extern _FRteps
-@ stub _FSinh
+@ cdecl _FSinh(float float)
 @ extern _FSnan
 # extern _FXbig
 @ cdecl -norelay _Getcoll()
@@ -7861,7 +7861,7 @@
 @ cdecl _Getwctypes(ptr ptr ptr ptr)
 @ extern _Hugeval
 @ extern _Inf
-@ stub _LCosh
+@ cdecl _LCosh(double double) _Cosh
 @ extern _LDenorm
 @ cdecl _LDscale(ptr long) _Dscale
 @ stub _LDtentox
@@ -7873,7 +7873,7 @@
 @ extern _LNan
 @ stub _LPoly
 # extern _LRteps
-@ stub _LSinh
+@ cdecl _LSinh(double double) _Sinh
 @ extern _LSnan
 # extern _LXbig
 @ extern _LZero
@@ -7886,7 +7886,7 @@
 @ stub _Once
 @ stub _Poly
 # extern _Rteps
-@ stub _Sinh
+@ cdecl _Sinh(double double)
 @ extern _Snan
 @ cdecl _Stod(ptr ptr long)
 @ cdecl _Stodx(ptr ptr long ptr)
diff --git a/dlls/msvcp90/math.c b/dlls/msvcp90/math.c
index baf61a472f1d..f413baf5b306 100644
--- a/dlls/msvcp90/math.c
+++ b/dlls/msvcp90/math.c
@@ -2363,6 +2363,38 @@ short __cdecl _FExp(float *x, float y, short scale)
     return dclass(*x);
 }
 
+/* ?_Cosh@?$_Ctr@N@std@@SANNN@Z
+   ?_Cosh@?$_Ctraits@N@std@@SANNN@Z
+   ?_Cosh@?$_Ctr@O@std@@SAOOO@Z
+   ?_Cosh@?$_Ctraits@O@std@@SAOOO@Z*/
+double __cdecl _Cosh(double x, double y)
+{
+    return y * cosh(x);
+}
+
+/* ?_Cosh@?$_Ctr@M@std@@SAMMM@Z
+   ?_Cosh@?$_Ctraits@M@std@@SAMMM@Z*/
+float __cdecl _FCosh(float x, float y)
+{
+    return y * cosh(x);
+}
+
+/* ?_Sinh@?$_Ctr@N@std@@SANNN@Z
+   ?_Sinh@?$_Ctraits@N@std@@SANNN@Z
+   ?_Sinh@?$_Ctr@O@std@@SAOOO@Z
+   ?_Sinh@?$_Ctraits@O@std@@SAOOO@Z */
+double __cdecl _Sinh(double x, double y)
+{
+    return y * sinh(x);
+}
+
+/* ?_Sinh@?$_Ctr@M@std@@SAMMM@Z
+   ?_Sinh@?$_Ctraits@M@std@@SAMMM@Z */
+float __cdecl _FSinh(float x, float y)
+{
+    return y * sinh(x);
+}
+
 /* ?_XLgamma@std@@YANN@Z */
 double __cdecl std__XLgamma_double(double z)
 {
diff --git a/dlls/msvcp90/msvcp90.spec b/dlls/msvcp90/msvcp90.spec
index 50fe6529e0f9..9eae0f8f8c47 100644
--- a/dlls/msvcp90/msvcp90.spec
+++ b/dlls/msvcp90/msvcp90.spec
@@ -2903,9 +2903,9 @@
 @ cdecl -arch=win64 ?_Copy_s@?$char_traits@G@std@@SAPEAGPEAG_KPEBG1@Z(ptr long ptr long) MSVCP_char_traits_short__Copy_s
 @ cdecl -arch=win32 ?_Copy_s@?$char_traits@_W@std@@SAPA_WPA_WIPB_WI@Z(ptr long ptr long) MSVCP_char_traits_wchar__Copy_s
 @ cdecl -arch=win64 ?_Copy_s@?$char_traits@_W@std@@SAPEA_WPEA_W_KPEB_W1@Z(ptr long ptr long) MSVCP_char_traits_wchar__Copy_s
-@ stub ?_Cosh@?$_Ctraits@M@std@@SAMMM@Z
-@ stub ?_Cosh@?$_Ctraits@N@std@@SANNN@Z
-@ stub ?_Cosh@?$_Ctraits@O@std@@SAOOO@Z
+@ cdecl ?_Cosh@?$_Ctraits@M@std@@SAMMM@Z(float float) _FCosh
+@ cdecl ?_Cosh@?$_Ctraits@N@std@@SANNN@Z(double double) _Cosh
+@ cdecl ?_Cosh@?$_Ctraits@O@std@@SAOOO@Z(double double) _Cosh
 @ cdecl -arch=win32 ?_Cout_func@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@XZ() _Cout_func
 @ cdecl -arch=win64 ?_Cout_func@std@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@1@XZ() _Cout_func
 @ stub -arch=win32 ?_Debug_message@std@@YAXPBD0@Z
@@ -3692,9 +3692,9 @@
 @ cdecl -arch=arm ?_Sgetn_s@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAAHPA_WIH@Z(ptr ptr long long) basic_streambuf_wchar__Sgetn_s
 @ thiscall -arch=i386 ?_Sgetn_s@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEHPA_WIH@Z(ptr ptr long long) basic_streambuf_wchar__Sgetn_s
 @ cdecl -arch=win64 ?_Sgetn_s@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QEAA_JPEA_W_K_J@Z(ptr ptr long long) basic_streambuf_wchar__Sgetn_s
-@ stub ?_Sinh@?$_Ctraits@M@std@@SAMMM@Z
-@ stub ?_Sinh@?$_Ctraits@N@std@@SANNN@Z
-@ stub ?_Sinh@?$_Ctraits@O@std@@SAOOO@Z
+@ cdecl ?_Sinh@?$_Ctraits@M@std@@SAMMM@Z(float float) _FSinh
+@ cdecl ?_Sinh@?$_Ctraits@N@std@@SANNN@Z(double double) _Sinh
+@ cdecl ?_Sinh@?$_Ctraits@O@std@@SAOOO@Z(double double) _Sinh
 @ extern -arch=arm ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IAAXPAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
 @ extern -arch=i386 ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IAEXPAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
 @ extern -arch=win64 ?_Stinit@?1??_Init@?$basic_filebuf@DU?$char_traits@D@std@@@std@@IEAAXPEAU_iobuf@@W4_Initfl@23@@Z@4HA basic_filebuf_char__Init__Stinit
@@ -7861,14 +7861,14 @@
 @ cdecl -arch=arm ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAAHPB_WH@Z(ptr ptr long) basic_streambuf_wchar_xsputn
 @ thiscall -arch=i386 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEHPB_WH@Z(ptr ptr long) basic_streambuf_wchar_xsputn
 @ cdecl -arch=win64 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MEAA_JPEB_W_J@Z(ptr ptr long) basic_streambuf_wchar_xsputn
-@ stub _Cosh
+@ cdecl _Cosh(double double)
 @ extern _Denorm
 @ stub _Dnorm
 @ cdecl _Dscale(ptr long)
 @ cdecl _Dtest(ptr)
 @ extern _Eps
 @ cdecl _Exp(ptr double long)
-@ stub _FCosh
+@ cdecl _FCosh(float float)
 @ extern _FDenorm
 @ stub _FDnorm
 @ cdecl _FDscale(ptr long)
@@ -7878,7 +7878,7 @@
 @ extern _FInf
 @ extern _FNan
 # extern _FRteps
-@ stub _FSinh
+@ cdecl _FSinh(float float)
 @ extern _FSnan
 # extern _FXbig
 @ cdecl -norelay _Getcoll()
@@ -7889,7 +7889,7 @@
 @ cdecl _Getwctypes(ptr ptr ptr ptr)
 @ extern _Hugeval
 @ extern _Inf
-@ stub _LCosh
+@ cdecl _LCosh(double double) _Cosh
 @ extern _LDenorm
 @ cdecl _LDscale(ptr long) _Dscale
 @ cdecl _LDtest(ptr) _Dtest
@@ -7899,7 +7899,7 @@
 @ extern _LNan
 @ stub _LPoly
 # extern _LRteps
-@ stub _LSinh
+@ cdecl _LSinh(double double) _Sinh
 @ extern _LSnan
 # extern _LXbig
 @ extern _LZero
@@ -7912,7 +7912,7 @@
 @ stub _Once
 @ stub _Poly
 # extern _Rteps
-@ stub _Sinh
+@ cdecl _Sinh(double double)
 @ extern _Snan
 @ cdecl _Stod(ptr ptr long)
 @ cdecl _Stodx(ptr ptr long ptr)
diff --git a/dlls/msvcp_win/msvcp_win.spec b/dlls/msvcp_win/msvcp_win.spec
index 69eb0a38040d..ae07b64ec930 100644
--- a/dlls/msvcp_win/msvcp_win.spec
+++ b/dlls/msvcp_win/msvcp_win.spec
@@ -3633,20 +3633,20 @@
 @ cdecl _Cnd_unregister_at_thread_exit(ptr) msvcp140._Cnd_unregister_at_thread_exit
 @ cdecl _Cnd_wait(ptr ptr) msvcp140._Cnd_wait
 @ cdecl _Copy_file(wstr wstr) msvcp140._Copy_file
-@ stub _Cosh
+@ cdecl _Cosh(double double) msvcp140._Cosh
 @ cdecl _Current_get(ptr) msvcp140._Current_get
 @ cdecl _Current_set(wstr) msvcp140._Current_set
 @ extern _Denorm msvcp140._Denorm
 @ cdecl _Dtest(ptr) msvcp140._Dtest
 @ cdecl _Equivalent(wstr wstr) msvcp140._Equivalent
 @ cdecl _Exp(ptr double long) msvcp140._Exp
-@ stub _FCosh
+@ cdecl _FCosh(float float) msvcp140._FCosh
 @ extern _FDenorm msvcp140._FDenorm
 @ cdecl _FDtest(ptr) msvcp140._FDtest
 @ cdecl _FExp(ptr float long) msvcp140._FExp
 @ extern _FInf msvcp140._FInf
 @ extern _FNan msvcp140._FNan
-@ stub _FSinh
+@ cdecl _FSinh(float float) msvcp140._FSinh
 @ extern _FSnan msvcp140._FSnan
 @ cdecl _File_size(wstr) msvcp140._File_size
 @ cdecl -norelay _Getcoll() msvcp140._Getcoll
@@ -3658,13 +3658,13 @@
 @ stub _Hard_links
 @ extern _Hugeval msvcp140._Hugeval
 @ extern _Inf msvcp140._Inf
-@ stub _LCosh
+@ cdecl _LCosh(double double) msvcp140._LCosh
 @ extern _LDenorm msvcp140._LDenorm
 @ cdecl _LDtest(ptr) msvcp140._LDtest
 @ stub _LExp
 @ extern _LInf msvcp140._LInf
 @ extern _LNan msvcp140._LNan
-@ stub _LSinh
+@ cdecl _LSinh(double double) msvcp140._LSinh
 @ extern _LSnan msvcp140._LSnan
 @ cdecl _Last_write_time(int64) msvcp140._Last_write_time
 @ cdecl _Link(wstr wstr) msvcp140._Link
@@ -3697,7 +3697,7 @@
 @ cdecl _Rename(wstr wstr) msvcp140._Rename
 @ cdecl _Resize(wstr int64) msvcp140._Resize
 @ cdecl _Set_last_write_time(wstr int64) msvcp140._Set_last_write_time
-@ stub _Sinh
+@ cdecl _Sinh(double double) msvcp140._Sinh
 @ extern _Snan msvcp140._Snan
 @ cdecl _Stat(wstr ptr) msvcp140._Stat
 @ stub _Statvfs
diff --git a/dlls/msvcrt/time.c b/dlls/msvcrt/time.c
index 9b0cbb4969f9..352a91fb058a 100644
--- a/dlls/msvcrt/time.c
+++ b/dlls/msvcrt/time.c
@@ -780,11 +780,12 @@ int CDECL _ftime32_s(struct __timeb32 *buf)
 __time64_t CDECL _time64(__time64_t *buf)
 {
     __time64_t curtime;
-    struct __timeb64 tb;
-
-    _ftime64(&tb);
+    FILETIME ft;
+    ULONGLONG time;
 
-    curtime = tb.time;
+    GetSystemTimeAsFileTime(&ft);
+    time = ((ULONGLONG)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
+    curtime = time / TICKSPERSEC - SECS_1601_TO_1970;
     return buf ? *buf = curtime : curtime;
 }
 
@@ -794,11 +795,10 @@ __time64_t CDECL _time64(__time64_t *buf)
 __time32_t CDECL _time32(__time32_t *buf)
 {
     __time32_t curtime;
-    struct __timeb64 tb;
-
-    _ftime64(&tb);
+    __time64_t time;
 
-    curtime = (tb.time == (__time32_t)tb.time) ? tb.time : -1;
+    time = _time64(NULL);
+    curtime = (time == (__time32_t)time) ? time : -1;
     return buf ? *buf = curtime : curtime;
 }
 
diff --git a/dlls/msxml3/Makefile.in b/dlls/msxml3/Makefile.in
index ff946de9fdd9..85cddced2b19 100644
--- a/dlls/msxml3/Makefile.in
+++ b/dlls/msxml3/Makefile.in
@@ -44,5 +44,6 @@ SOURCES = \
 	xmlparser.c \
 	xmlparser.idl \
 	xmlview.c \
+	xpath.c \
 	xslpattern.l \
 	xslpattern.y
diff --git a/dlls/msxml3/domdoc.c b/dlls/msxml3/domdoc.c
index 252135b4f8d3..4185229a0971 100644
--- a/dlls/msxml3/domdoc.c
+++ b/dlls/msxml3/domdoc.c
@@ -42,6 +42,7 @@
 #include "wine/debug.h"
 
 #include "msxml_private.h"
+#include "xpath.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
 
@@ -138,29 +139,18 @@ static inline ConnectionPoint *impl_from_IConnectionPoint(IConnectionPoint *ifac
     return CONTAINING_RECORD(iface, ConnectionPoint, IConnectionPoint_iface);
 }
 
-int registerNamespaces(struct domnode *node, xmlXPathContextPtr ctxt)
+void domdoc_properties_clear_selection_namespaces(struct domdoc_properties *properties)
 {
-    struct domnode *doc = node->owner ? node->owner : node;
-    const select_ns_entry* ns = NULL;
-    int n = 0;
+    struct xpath_namespace *ns, *next;
 
-    LIST_FOR_EACH_ENTRY( ns, &doc->properties->selectNsList, select_ns_entry, entry )
-    {
-        xmlXPathRegisterNs(ctxt, ns->prefix, ns->href);
-        ++n;
-    }
-
-    return n;
-}
-
-void domdoc_properties_clear_selection_namespaces(struct list *pNsList)
-{
-    select_ns_entry *ns, *ns2;
-    LIST_FOR_EACH_ENTRY_SAFE( ns, ns2, pNsList, select_ns_entry, entry )
+    LIST_FOR_EACH_ENTRY_SAFE(ns, next, &properties->namespaces.entries, struct xpath_namespace, entry)
     {
+        free(ns->prefix);
+        free(ns->uri);
         free(ns);
     }
-    list_init(pNsList);
+
+    list_init(&properties->namespaces.entries);
 }
 
 static void release_namespaces(domdoc *doc)
@@ -840,19 +830,6 @@ static HRESULT WINAPI domdoc_get_implementation(IXMLDOMDocument3 *iface, IXMLDOM
     return create_dom_implementation(impl);
 }
 
-static struct domnode * domdoc_get_document_element(struct domdoc *doc)
-{
-    struct domnode *node;
-
-    LIST_FOR_EACH_ENTRY(node, &doc->node->children, struct domnode, entry)
-    {
-        if (node->type == NODE_ELEMENT)
-            return node;
-    }
-
-    return NULL;
-}
-
 static HRESULT WINAPI domdoc_get_documentElement(IXMLDOMDocument3 *iface, IXMLDOMElement **ret)
 {
     domdoc *doc = impl_from_IXMLDOMDocument3(iface);
@@ -867,7 +844,7 @@ static HRESULT WINAPI domdoc_get_documentElement(IXMLDOMDocument3 *iface, IXMLDO
 
     *ret = NULL;
 
-    if ((element = domdoc_get_document_element(doc)))
+    if ((element = domnode_get_root_element(doc->node)))
     {
         if (SUCCEEDED(hr = create_node(element, &node)))
         {
@@ -889,7 +866,7 @@ static HRESULT WINAPI domdoc_putref_documentElement(IXMLDOMDocument3 *iface, IXM
 
     TRACE("%p, %p.\n", iface, element);
 
-    if ((current = domdoc_get_document_element(doc)))
+    if ((current = domnode_get_root_element(doc->node)))
     {
         if (SUCCEEDED(hr = create_node(current, &node)))
         {
@@ -1857,131 +1834,19 @@ static HRESULT WINAPI domdoc_setProperty(IXMLDOMDocument3 *iface, BSTR p, VARIAN
         VariantClear(&varStr);
         return hr;
     }
-    else if (!wcsicmp(p, L"SelectionNamespaces"))
-    {
-        xmlChar *nsStr = (xmlChar *)properties->selectNsStr;
-        struct list *pNsList;
-        VARIANT varStr;
-        BSTR bstr;
 
-        V_VT(&varStr) = VT_EMPTY;
+    if (!wcscmp(p, L"SelectionNamespaces"))
+    {
         if (V_VT(&value) != VT_BSTR)
-        {
-            if (FAILED(hr = VariantChangeType(&varStr, &value, 0, VT_BSTR)))
-                return hr;
-            bstr = V_BSTR(&varStr);
-        }
-        else
-            bstr = V_BSTR(&value);
-
-        hr = S_OK;
-
-        pNsList = &properties->selectNsList;
-        domdoc_properties_clear_selection_namespaces(pNsList);
-        free(nsStr);
-        nsStr = xmlchar_from_wchar(bstr);
-
-        TRACE("property value: \"%s\"\n", debugstr_w(bstr));
-
-        properties->selectNsStr = nsStr;
-        properties->selectNsStr_len = xmlStrlen(nsStr);
-        if (bstr && *bstr)
-        {
-            xmlChar *pTokBegin, *pTokEnd, *pTokInner;
-            select_ns_entry* ns_entry = NULL;
-
-            pTokBegin = nsStr;
-
-            /* skip leading spaces */
-            while (*pTokBegin == ' '  || *pTokBegin == '\n' ||
-                   *pTokBegin == '\t' || *pTokBegin == '\r')
-                ++pTokBegin;
-
-            for (; *pTokBegin; pTokBegin = pTokEnd)
-            {
-                if (ns_entry)
-                    memset(ns_entry, 0, sizeof(select_ns_entry));
-                else
-                    ns_entry = calloc(1, sizeof(select_ns_entry));
-
-                while (*pTokBegin == ' ')
-                    ++pTokBegin;
-                pTokEnd = pTokBegin;
-                while (*pTokEnd != ' ' && *pTokEnd != 0)
-                    ++pTokEnd;
-
-                /* so it failed to advance which means we've got some trailing spaces */
-                if (pTokEnd == pTokBegin) break;
-
-                if (xmlStrncmp(pTokBegin, (xmlChar const*)"xmlns", 5) != 0)
-                {
-                    hr = E_FAIL;
-                    WARN("Syntax error in xmlns string: %s\n\tat token: %s\n",
-                          debugstr_w(bstr), debugstr_an((const char*)pTokBegin, pTokEnd-pTokBegin));
-                    continue;
-                }
-
-                pTokBegin += 5;
-                if (*pTokBegin == '=')
-                {
-                    /*valid for XSLPattern?*/
-                    FIXME("Setting default xmlns not supported - skipping.\n");
-                    continue;
-                }
-                else if (*pTokBegin == ':')
-                {
-                    ns_entry->prefix = ++pTokBegin;
-                    for (pTokInner = pTokBegin; pTokInner != pTokEnd && *pTokInner != '='; ++pTokInner)
-                        ;
-
-                    if (pTokInner == pTokEnd)
-                    {
-                        hr = E_FAIL;
-                        WARN("Syntax error in xmlns string: %s\n\tat token: %s\n",
-                              debugstr_w(bstr), debugstr_an((const char*)pTokBegin, pTokEnd-pTokBegin));
-                        continue;
-                    }
-
-                    ns_entry->prefix_end = *pTokInner;
-                    *pTokInner = 0;
-                    ++pTokInner;
-
-                    if (pTokEnd-pTokInner > 1 &&
-                        ((*pTokInner == '\'' && *(pTokEnd-1) == '\'') ||
-                         (*pTokInner == '"' && *(pTokEnd-1) == '"')))
-                    {
-                        ns_entry->href = ++pTokInner;
-                        ns_entry->href_end = *(pTokEnd-1);
-                        *(pTokEnd-1) = 0;
-                        list_add_tail(pNsList, &ns_entry->entry);
-
-                        ns_entry = NULL;
-                        continue;
-                    }
-                    else
-                    {
-                        WARN("Syntax error in xmlns string: %s\n\tat token: %s\n",
-                              debugstr_w(bstr), debugstr_an((const char*)pTokInner, pTokEnd-pTokInner));
-                        list_add_tail(pNsList, &ns_entry->entry);
-
-                        ns_entry = NULL;
-                        hr = E_FAIL;
-                        continue;
-                    }
-                }
-                else
-                {
-                    hr = E_FAIL;
-                    continue;
-                }
-            }
-            free(ns_entry);
-        }
+            return E_FAIL;
 
-        VariantClear(&varStr);
-        return hr;
+        domdoc_properties_clear_selection_namespaces(properties);
+        SysFreeString(properties->namespaces.value);
+        properties->namespaces.value = SysAllocString(V_BSTR(&value));
+        return xpath_parse_selection_namespaces(properties->namespaces.value, properties);
     }
-    else if (wcsicmp(p, L"ValidateOnParse") == 0)
+
+    if (wcsicmp(p, L"ValidateOnParse") == 0)
     {
         if (properties->version < MSXML4)
             return E_FAIL;
@@ -2057,41 +1922,6 @@ static HRESULT WINAPI domdoc_getProperty(IXMLDOMDocument3 *iface, BSTR p, VARIAN
         V_BSTR(var) = SysAllocString(properties->XPath ? L"XPath" : L"XSLPattern");
         return V_BSTR(var) ? S_OK : E_OUTOFMEMORY;
     }
-    else if (!wcsicmp(p, L"SelectionNamespaces"))
-    {
-        int lenA, lenW;
-        BSTR rebuiltStr, cur;
-        const xmlChar *nsStr;
-        struct list *pNsList;
-        select_ns_entry* pNsEntry;
-
-        V_VT(var) = VT_BSTR;
-        nsStr = properties->selectNsStr;
-        pNsList = &properties->selectNsList;
-        lenA = properties->selectNsStr_len;
-        lenW = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)nsStr, lenA+1, NULL, 0);
-        rebuiltStr = malloc(lenW * sizeof(WCHAR));
-        MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)nsStr, lenA+1, rebuiltStr, lenW);
-        cur = rebuiltStr;
-        /* this is fine because all of the chars that end tokens are ASCII*/
-        LIST_FOR_EACH_ENTRY(pNsEntry, pNsList, select_ns_entry, entry)
-        {
-            while (*cur != 0) ++cur;
-            if (pNsEntry->prefix_end)
-            {
-                *cur = pNsEntry->prefix_end;
-                while (*cur != 0) ++cur;
-            }
-
-            if (pNsEntry->href_end)
-            {
-                *cur = pNsEntry->href_end;
-            }
-        }
-        V_BSTR(var) = SysAllocString(rebuiltStr);
-        free(rebuiltStr);
-        return S_OK;
-    }
     else if (!wcsicmp(p, L"ValidateOnParse"))
     {
         if (properties->version < MSXML4)
@@ -2104,6 +1934,14 @@ static HRESULT WINAPI domdoc_getProperty(IXMLDOMDocument3 *iface, BSTR p, VARIAN
         }
     }
 
+    if (!wcscmp(p, L"SelectionNamespaces"))
+    {
+        V_VT(var) = VT_BSTR;
+        V_BSTR(var) = SysAllocString(properties->namespaces.value ?
+                properties->namespaces.value : L"");
+        return V_BSTR(var) ? S_OK : E_OUTOFMEMORY;
+    }
+
     if (!wcscmp(p, L"ProhibitDTD"))
     {
         V_VT(var) = VT_BOOL;
diff --git a/dlls/msxml3/msxml_private.h b/dlls/msxml3/msxml_private.h
index 794053c53901..c10a7defc2ee 100644
--- a/dlls/msxml3/msxml_private.h
+++ b/dlls/msxml3/msxml_private.h
@@ -88,7 +88,6 @@ enum error_codes
     E_SAX_MAX_ELEMENT_DEPTH = 0xc00cee92,
 };
 
-
 static inline bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size)
 {
     size_t new_capacity, max_capacity;
@@ -193,26 +192,23 @@ enum domnode_flags
     DOMNODE_IGNORED_WS_AFTER_STARTTAG = 0x2,
     DOMNODE_IGNORED_WS = 0x4,
     DOMNODE_PARSED_VALUE = 0x8,
+    DOMNODE_NS_DECL = 0x10,
+    DOMNODE_NO_PARENT = 0x20,
 };
 
-typedef struct _select_ns_entry
-{
-    struct list entry;
-    xmlChar const* prefix;
-    xmlChar prefix_end;
-    xmlChar const* href;
-    xmlChar href_end;
-} select_ns_entry;
-
 struct domdoc_properties
 {
     MSXML_VERSION version;
     VARIANT_BOOL preserving;
     VARIANT_BOOL validating;
     IXMLDOMSchemaCollection2* schemaCache;
-    struct list selectNsList;
-    xmlChar const* selectNsStr;
-    LONG selectNsStr_len;
+
+    struct
+    {
+        struct list entries;
+        BSTR value;
+    } namespaces;
+
     bool XPath;
     bool prohibit_dtd;
     bool normalize_attribute_values;
@@ -251,7 +247,7 @@ struct domnode
 extern IXMLDOMSchemaCollection2 *doc_properties_get_schema(struct domdoc_properties *properties);
 extern MSXML_VERSION doc_properties_get_version(struct domdoc_properties *properties);
 
-extern void domdoc_properties_clear_selection_namespaces(struct list *);
+extern void domdoc_properties_clear_selection_namespaces(struct domdoc_properties *);
 extern struct domdoc_properties *domdoc_create_properties(MSXML_VERSION version);
 extern MSXML_VERSION domdoc_version(const struct domnode *doc);
 extern HRESULT domnode_create(DOMNodeType type, const WCHAR *name, int name_len,
@@ -259,9 +255,15 @@ extern HRESULT domnode_create(DOMNodeType type, const WCHAR *name, int name_len,
 extern void domnode_destroy_tree(struct domnode *tree);
 extern struct domnode *domnode_addref(struct domnode *node);
 extern void domnode_release(struct domnode *node);
+extern struct domnode *domnode_get_root_element(struct domnode *doc);
+extern struct domnode *domnode_get_first_attribute(struct domnode *node);
+extern struct domnode *domnode_get_next_attribute_sibling(struct domnode *node);
 extern struct domnode *domnode_get_first_child(struct domnode *node);
+extern struct domnode *domnode_get_last_child(struct domnode *node);
 extern struct domnode *domnode_get_next_sibling(struct domnode *node);
+extern struct domnode *domnode_get_previous_sibling(struct domnode *node);
 extern HRESULT domnode_get_attribute(const struct domnode *node, const WCHAR *name, struct domnode **attr);
+extern bool domnode_is_namespace_declaration(const struct domnode *node);
 extern HRESULT node_clone_domnode(struct domnode *, bool, struct domnode **);
 extern HRESULT parse_stream(ISequentialStream *stream, bool utf16, const struct domdoc_properties *properties,
         struct domnode **tree);
@@ -283,6 +285,8 @@ static inline bool xml_is_space(WCHAR ch)
 {
     return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
 }
+bool xml_is_ncname_startchar(WCHAR ch);
+bool xml_is_ncnamechar(WCHAR ch);
 
 /* IXMLDOMNamedNodeMap custom function table */
 struct nodemap_funcs
diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c
index 8d1c8418ca66..e72ebc9132c4 100644
--- a/dlls/msxml3/node.c
+++ b/dlls/msxml3/node.c
@@ -49,6 +49,7 @@
 
 #include "msxml_private.h"
 #include "saxreader_extensions.h"
+#include "xpath.h"
 
 #include "wine/debug.h"
 
@@ -358,12 +359,35 @@ static struct domnode *node_from_entry(struct list *entry)
     return entry ? LIST_ENTRY(entry, struct domnode, entry) : NULL;
 }
 
+struct domnode * domnode_get_root_element(struct domnode *doc)
+{
+    struct domnode *node;
+
+    LIST_FOR_EACH_ENTRY(node, &doc->children, struct domnode, entry)
+    {
+        if (node->type == NODE_ELEMENT)
+            return node;
+    }
+
+    return NULL;
+}
+
 struct domnode *domnode_get_first_child(struct domnode *node)
 {
     return node_from_entry(list_head(&node->children));
 }
 
-static struct domnode *domnode_get_previous_sibling(struct domnode *node)
+struct domnode *domnode_get_last_child(struct domnode *node)
+{
+    return node_from_entry(list_tail(&node->children));
+}
+
+struct domnode *domnode_get_first_attribute(struct domnode *node)
+{
+    return node_from_entry(list_head(&node->attributes));
+}
+
+struct domnode *domnode_get_previous_sibling(struct domnode *node)
 {
     if (node->parent)
         return node_from_entry(list_prev(&node->parent->children, &node->entry));
@@ -378,7 +402,7 @@ HRESULT node_get_first_child(struct domnode *node, IXMLDOMNode **ret)
 
 HRESULT node_get_last_child(struct domnode *node, IXMLDOMNode **ret)
 {
-    return get_node(node_from_entry(list_tail(&node->children)), ret);
+    return get_node(domnode_get_last_child(node), ret);
 }
 
 HRESULT node_get_previous_sibling(struct domnode *node, IXMLDOMNode **ret)
@@ -398,6 +422,14 @@ struct domnode *domnode_get_next_sibling(struct domnode *node)
     return NULL;
 }
 
+struct domnode *domnode_get_next_attribute_sibling(struct domnode *node)
+{
+    if (node->parent)
+        return node_from_entry(list_next(&node->parent->attributes, &node->entry));
+
+    return NULL;
+}
+
 HRESULT node_get_next_sibling(struct domnode *node, IXMLDOMNode **ret)
 {
     return get_node(domnode_get_next_sibling(node), ret);
@@ -1041,10 +1073,40 @@ HRESULT node_remove_attribute(struct domnode *node, const WCHAR *name, IXMLDOMNo
     return hr;
 }
 
+static bool is_same_namespace_prefix(const struct domnode *node, const WCHAR *prefix)
+{
+    if (node->prefix)
+        return prefix && !wcscmp(node->prefix, prefix);
+
+    return !prefix;
+}
+
+static bool domnode_has_namespace_declaration_name(const struct domnode *node)
+{
+    if (!wcscmp(node->qname, L"xmlns"))
+        return true;
+    if (is_same_namespace_prefix(node, L"xmlns"))
+        return true;
+    return false;
+}
+
+static void domnode_insert_attribute(struct domnode *parent, struct domnode *node, struct domnode *ref_node)
+{
+    domnode_unlink_attribute(node);
+
+    if (ref_node)
+        list_add_before(&ref_node->entry, &node->entry);
+    else
+        list_add_tail(&parent->attributes, &node->entry);
+    node->parent = parent;
+    domnode_add_refs(node->parent, node->refcount);
+    domnode_set_owner(node, parent);
+}
+
 HRESULT domnode_create(DOMNodeType type, const WCHAR *name, int name_len, const WCHAR *uri, int uri_len,
         struct domnode *owner, struct domnode **node)
 {
-    struct domnode *object;
+    struct domnode *object, *xmlns_xml;
     WCHAR *p;
 
     *node = NULL;
@@ -1102,6 +1164,17 @@ HRESULT domnode_create(DOMNodeType type, const WCHAR *name, int name_len, const
             if (!wcscmp(object->name, L"xml"))
                 object->flags |= DOMNODE_READONLY_VALUE;
             break;
+        case NODE_ATTRIBUTE:
+            if (domnode_has_namespace_declaration_name(object))
+                object->flags |= DOMNODE_NS_DECL;
+            break;
+        case NODE_DOCUMENT:
+            /* Stash implicit namespace as a document attribute, without a parent. */
+            domnode_create(NODE_ATTRIBUTE, L"xmlns:xml", 9, NULL, 0, object, &xmlns_xml);
+            node_put_data(xmlns_xml, L"http://www.w3.org/XML/1998/namespace");
+            xmlns_xml->flags |= DOMNODE_READONLY_VALUE | DOMNODE_NO_PARENT;
+            domnode_insert_attribute(object, xmlns_xml, NULL);
+            break;
         default:
             ;
     }
@@ -1111,19 +1184,6 @@ HRESULT domnode_create(DOMNodeType type, const WCHAR *name, int name_len, const
     return S_OK;
 }
 
-static void domnode_insert_attribute(struct domnode *parent, struct domnode *node, struct domnode *ref_node)
-{
-    domnode_unlink_attribute(node);
-
-    if (ref_node)
-        list_add_before(&ref_node->entry, &node->entry);
-    else
-        list_add_tail(&parent->attributes, &node->entry);
-    node->parent = parent;
-    domnode_add_refs(node->parent, node->refcount);
-    domnode_set_owner(node, parent);
-}
-
 static HRESULT parse_xml_decl_append_attribute(struct domnode *pi, const WCHAR *name, BSTR value)
 {
     struct domnode *attribute;
@@ -1243,14 +1303,12 @@ struct domnode *domnode_addref(struct domnode *node)
 
 struct domdoc_properties *domdoc_create_properties(MSXML_VERSION version)
 {
-    struct domdoc_properties *properties = malloc(sizeof(*properties));
+    struct domdoc_properties *properties = calloc(1, sizeof(*properties));
 
-    list_init(&properties->selectNsList);
+    list_init(&properties->namespaces.entries);
     properties->preserving = VARIANT_FALSE;
     properties->validating = VARIANT_TRUE;
     properties->schemaCache = NULL;
-    properties->selectNsStr = calloc(1, sizeof(xmlChar));
-    properties->selectNsStr_len = 0;
 
     /* properties that are dependent on object versions */
     properties->version = version;
@@ -1259,9 +1317,6 @@ struct domdoc_properties *domdoc_create_properties(MSXML_VERSION version)
     properties->normalize_attribute_values = false;
     properties->max_element_depth = version > MSXML3 ? 256 : 5000;
 
-    /* document uri */
-    properties->uri = NULL;
-
     return properties;
 }
 
@@ -1269,8 +1324,9 @@ static void domdoc_properties_destroy(struct domdoc_properties *properties)
 {
     if (properties->schemaCache)
         IXMLDOMSchemaCollection2_Release(properties->schemaCache);
-    domdoc_properties_clear_selection_namespaces(&properties->selectNsList);
-    free((xmlChar*)properties->selectNsStr);
+    domdoc_properties_clear_selection_namespaces(properties);
+    SysFreeString(properties->namespaces.value);
+    properties->namespaces.value = NULL;
     if (properties->uri)
         IUri_Release(properties->uri);
     free(properties);
@@ -1279,10 +1335,6 @@ static void domdoc_properties_destroy(struct domdoc_properties *properties)
 static struct domdoc_properties* domdoc_properties_clone(struct domdoc_properties const* properties)
 {
     struct domdoc_properties* pcopy = malloc(sizeof(*pcopy));
-    select_ns_entry const* ns = NULL;
-    select_ns_entry* new_ns = NULL;
-    int len = (properties->selectNsStr_len+1)*sizeof(xmlChar);
-    ptrdiff_t offset;
 
     if (pcopy)
     {
@@ -1296,20 +1348,10 @@ static struct domdoc_properties* domdoc_properties_clone(struct domdoc_propertie
             IXMLDOMSchemaCollection2_AddRef(pcopy->schemaCache);
         pcopy->XPath = properties->XPath;
         pcopy->max_element_depth = properties->max_element_depth;
-        pcopy->selectNsStr_len = properties->selectNsStr_len;
-        list_init( &pcopy->selectNsList );
-        pcopy->selectNsStr = malloc(len);
-        memcpy((xmlChar*)pcopy->selectNsStr, properties->selectNsStr, len);
-        offset = pcopy->selectNsStr - properties->selectNsStr;
-
-        LIST_FOR_EACH_ENTRY( ns, (&properties->selectNsList), select_ns_entry, entry )
-        {
-            new_ns = malloc(sizeof(select_ns_entry));
-            memcpy(new_ns, ns, sizeof(select_ns_entry));
-            new_ns->href += offset;
-            new_ns->prefix += offset;
-            list_add_tail(&pcopy->selectNsList, &new_ns->entry);
-        }
+        list_init(&pcopy->namespaces.entries);
+        pcopy->namespaces.value = SysAllocString(properties->namespaces.value);
+        if (pcopy->namespaces.value)
+            xpath_parse_selection_namespaces(pcopy->namespaces.value, pcopy);
 
         pcopy->uri = properties->uri;
         if (pcopy->uri)
@@ -1887,21 +1929,9 @@ static void node_dump_qualified_name(struct node_dump_context *context, struct d
     node_dump_append(context, node->qname, SysStringLen(node->qname));
 }
 
-static bool is_same_namespace_prefix(const struct domnode *node, const WCHAR *prefix)
+bool domnode_is_namespace_declaration(const struct domnode *node)
 {
-    if (node->prefix)
-        return prefix && !wcscmp(node->prefix, prefix);
-
-    return !prefix;
-}
-
-static bool is_namespace_definition(struct domnode *node)
-{
-    if (!wcscmp(node->qname, L"xmlns"))
-        return true;
-    if (is_same_namespace_prefix(node, L"xmlns"))
-        return true;
-    return false;
+    return node->flags & DOMNODE_NS_DECL;
 }
 
 static bool is_namespace_defined(struct node_dump_context *context, struct domnode *node)
@@ -1951,7 +1981,7 @@ static void node_dump_element_attributes(struct node_dump_context *context, stru
     /* Collect explicitly defined namespaces */
     LIST_FOR_EACH_ENTRY(attr, &node->attributes, struct domnode, entry)
     {
-        if (is_namespace_definition(attr))
+        if (domnode_is_namespace_declaration(attr))
         {
             node_get_text(attr, &text);
             node_dump_push_namespace(context, node_dump_get_namespace_prefix(attr), text, true);
@@ -1966,7 +1996,7 @@ static void node_dump_element_attributes(struct node_dump_context *context, stru
 
     LIST_FOR_EACH_ENTRY(attr, &node->attributes, struct domnode, entry)
     {
-        if (is_namespace_definition(attr))
+        if (domnode_is_namespace_declaration(attr))
             continue;
 
         if (!is_namespace_defined(context, attr))
@@ -3523,8 +3553,8 @@ HRESULT node_set_attribute_value(struct domnode *node, const WCHAR *name, const
         hr = node_put_data(attr, attr_value);
     VariantClear(&v);
 
-    /* Allow setting namespace definition node once. */
-    if (attr && is_namespace_definition(attr))
+    /* Allow setting namespace declaration node once. */
+    if (attr && domnode_is_namespace_declaration(attr))
         attr->flags |= DOMNODE_READONLY_VALUE;
 
     return hr;
@@ -3821,7 +3851,7 @@ static HRESULT WINAPI parse_content_handler_startElement(ISAXContentHandler *ifa
             if (attr)
             {
                 attr->flags |= DOMNODE_PARSED_VALUE;
-                if (is_namespace_definition(attr))
+                if (domnode_is_namespace_declaration(attr))
                     attr->flags |= DOMNODE_READONLY_VALUE;
             }
         }
@@ -4806,3 +4836,37 @@ HRESULT node_get_data_length(struct domnode *node, LONG *length)
     *length = SysStringLen(node->data);
     return S_OK;
 }
+
+WCHAR *xpath_translate_function(const WCHAR *to, const WCHAR *from, const WCHAR *str)
+{
+    struct string_buffer buffer;
+    const WCHAR *point, *cptr;
+    int offset, max;
+    int ch;
+
+    string_buffer_init(&buffer);
+
+    max = wcslen(to);
+    for (cptr = str; (ch = *cptr);)
+    {
+        point = wcschr(from, *cptr);
+        offset = point ? point - from : -1;
+
+        if (offset >= 0)
+        {
+            if (offset < max)
+                string_append(&buffer, &to[offset], 1);
+        }
+        else
+        {
+            string_append(&buffer, cptr, 1);
+        }
+
+        cptr++;
+    }
+
+    if (buffer.status != S_OK)
+        string_buffer_cleanup(&buffer);
+
+    return buffer.data;
+}
diff --git a/dlls/msxml3/saxreader.c b/dlls/msxml3/saxreader.c
index c69fe294b941..366f779b0637 100644
--- a/dlls/msxml3/saxreader.c
+++ b/dlls/msxml3/saxreader.c
@@ -657,7 +657,7 @@ static int convert_utf16be(UINT cp, const char *src, int src_size, WCHAR *buffer
     if (!buffer) return src_size / 2;
 
     for (int i = 0; i < size; ++i)
-        buffer[i] = src[i + 1] | src[i];
+        buffer[i] = src[2 * i + 1] | (src[2 * i] << 8);
 
     return size;
 }
@@ -3356,43 +3356,6 @@ static void saxreader_parse_sddecl(struct saxlocator *locator, const struct pars
         saxreader_set_error(locator, E_SAX_INVALID_STANDALONE);
 }
 
-/*
-static bool saxreader_parse_xmldecl(struct saxlocator *locator)
-{
-    WCHAR ch;
-
-    saxreader_more(locator);
-
-    ch = saxreader_get_char(locator, 5);
-    if (!saxreader_peek(locator, L"<?xml", 5) || !saxreader_is_space(ch))
-        return false;
-    saxreader_skip(locator, 5);
-    saxreader_skipspaces(locator);
-
-    saxreader_parse_versioninfo(locator);
-
-    if (saxreader_cmp(locator, L"?>"))
-        return true;
-
-    saxreader_parse_encdecl(locator);
-
-    if (locator->saxreader->xmldecl_encoding)
-    {
-        if (saxreader_cmp(locator, L"?>"))
-            return true;
-        saxreader_skip_required_spaces(locator);
-    }
-
-    saxreader_skipspaces(locator);
-    saxreader_parse_sddecl(locator);
-    saxreader_skipspaces(locator);
-
-    if (!saxreader_cmp(locator, L"?>"))
-        saxreader_set_error(locator, E_SAX_BAD_XMLDECL);
-
-    return true;
-}
-*/
 static BSTR saxreader_parse_xmldecl_attribute(struct saxlocator *locator, struct parsed_name *name);
 
 /* [85] BaseChar ::= ... */
@@ -3580,17 +3543,27 @@ static bool saxreader_is_namechar(WCHAR ch)
 }
 
 /* [5] NCNameChar ::= NameChar - ':' */
-static bool saxreader_is_ncnamechar(WCHAR ch)
+bool xml_is_ncnamechar(WCHAR ch)
 {
     return saxreader_is_namechar(ch) && ch != ':';
 }
 
+static bool saxreader_is_ncnamechar(WCHAR ch)
+{
+    return xml_is_ncnamechar(ch);
+}
+
 /* [6] NCNameStartChar ::= Letter | '_' */
-static bool saxreader_is_ncname_startchar(WCHAR ch)
+bool xml_is_ncname_startchar(WCHAR ch)
 {
     return saxreader_is_letter(ch) || ch == '_';
 }
 
+static bool saxreader_is_ncname_startchar(WCHAR c)
+{
+    return xml_is_ncname_startchar(c);
+}
+
 static void *saxreader_calloc(struct saxlocator *locator, size_t count, size_t size)
 {
     void *ret;
@@ -5906,10 +5879,6 @@ static enum xmlencoding saxreader_match_encoding(const char *data, size_t size,
         return XML_ENCODING_UTF16LE;
     if (b[0] == 0 && b[1] == '<' && b[2] == 0 && b[3] == '?')
         return XML_ENCODING_UTF16BE;
-    if (b[0] == '<' && b[1] == '?' && b[2] == 'x' && b[3] == 'm')
-        return XML_ENCODING_UTF8;
-    if (b[0] == '<' && b[1] && b[1] != '?')
-        return XML_ENCODING_UTF8;
 
     if (b[0] == 0xef && b[1] == 0xbb && b[2] == 0xbf)
     {
@@ -5929,7 +5898,7 @@ static enum xmlencoding saxreader_match_encoding(const char *data, size_t size,
         return XML_ENCODING_UTF16LE;
     }
 
-    return XML_ENCODING_UNKNOWN;
+    return XML_ENCODING_UTF8;
 }
 
 static void saxreader_detect_encoding(struct saxlocator *locator, bool force_utf16)
diff --git a/dlls/msxml3/selection.c b/dlls/msxml3/selection.c
index 3acf7b92c62d..159bf86f4e47 100644
--- a/dlls/msxml3/selection.c
+++ b/dlls/msxml3/selection.c
@@ -37,6 +37,7 @@
 #include "msxml2did.h"
 
 #include "msxml_private.h"
+#include "xpath.h"
 
 #include "wine/debug.h"
 
@@ -51,7 +52,6 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
 
-int registerNamespaces(struct domnode *node, xmlXPathContextPtr ctxt);
 xmlChar* XSLPattern_to_XPath(xmlXPathContextPtr ctxt, xmlChar const* xslpat_str);
 
 typedef struct
@@ -598,184 +598,65 @@ static dispex_static_data_t domselection_dispex =
     domselection_iface_tids
 };
 
-#define XSLPATTERN_CHECK_ARGS(n) \
-    if (nargs != n) { \
-        FIXME("XSLPattern syntax error: Expected %i arguments, got %i\n", n, nargs); \
-        xmlXPathSetArityError(pctx); \
-        return; \
-    }
-
-static void XSLPattern_index(xmlXPathParserContextPtr pctx, int nargs)
-{
-    XSLPATTERN_CHECK_ARGS(0);
-
-    xmlXPathPositionFunction(pctx, 0);
-    xmlXPathReturnNumber(pctx, xmlXPathPopNumber(pctx) - 1.0);
-}
-
-static void XSLPattern_end(xmlXPathParserContextPtr pctx, int nargs)
-{
-    double pos, last;
-    XSLPATTERN_CHECK_ARGS(0);
-
-    xmlXPathPositionFunction(pctx, 0);
-    pos = xmlXPathPopNumber(pctx);
-    xmlXPathLastFunction(pctx, 0);
-    last = xmlXPathPopNumber(pctx);
-    xmlXPathReturnBoolean(pctx, pos == last);
-}
-
-static void XSLPattern_nodeType(xmlXPathParserContextPtr pctx, int nargs)
-{
-    XSLPATTERN_CHECK_ARGS(0);
-    xmlXPathReturnNumber(pctx, pctx->context->node->type);
-}
-
-static void XSLPattern_OP_IEq(xmlXPathParserContextPtr pctx, int nargs)
-{
-    xmlChar *arg1, *arg2;
-    XSLPATTERN_CHECK_ARGS(2);
-
-    arg2 = xmlXPathPopString(pctx);
-    arg1 = xmlXPathPopString(pctx);
-    xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) == 0);
-    xmlFree(arg1);
-    xmlFree(arg2);
-}
-
-static void XSLPattern_OP_INEq(xmlXPathParserContextPtr pctx, int nargs)
-{
-    xmlChar *arg1, *arg2;
-    XSLPATTERN_CHECK_ARGS(2);
-
-    arg2 = xmlXPathPopString(pctx);
-    arg1 = xmlXPathPopString(pctx);
-    xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) != 0);
-    xmlFree(arg1);
-    xmlFree(arg2);
-}
-
-static void XSLPattern_OP_ILt(xmlXPathParserContextPtr pctx, int nargs)
-{
-    xmlChar *arg1, *arg2;
-    XSLPATTERN_CHECK_ARGS(2);
-
-    arg2 = xmlXPathPopString(pctx);
-    arg1 = xmlXPathPopString(pctx);
-    xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) < 0);
-    xmlFree(arg1);
-    xmlFree(arg2);
-}
-
-static void XSLPattern_OP_ILEq(xmlXPathParserContextPtr pctx, int nargs)
-{
-    xmlChar *arg1, *arg2;
-    XSLPATTERN_CHECK_ARGS(2);
-
-    arg2 = xmlXPathPopString(pctx);
-    arg1 = xmlXPathPopString(pctx);
-    xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) <= 0);
-    xmlFree(arg1);
-    xmlFree(arg2);
-}
-
-static void XSLPattern_OP_IGt(xmlXPathParserContextPtr pctx, int nargs)
-{
-    xmlChar *arg1, *arg2;
-    XSLPATTERN_CHECK_ARGS(2);
-
-    arg2 = xmlXPathPopString(pctx);
-    arg1 = xmlXPathPopString(pctx);
-    xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) > 0);
-    xmlFree(arg1);
-    xmlFree(arg2);
-}
-
-static void XSLPattern_OP_IGEq(xmlXPathParserContextPtr pctx, int nargs)
+static void xmlRegisterNamespaces(struct domnode *node, xmlXPathContextPtr ctxt)
 {
-    xmlChar *arg1, *arg2;
-    XSLPATTERN_CHECK_ARGS(2);
-
-    arg2 = xmlXPathPopString(pctx);
-    arg1 = xmlXPathPopString(pctx);
-    xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) >= 0);
-    xmlFree(arg1);
-    xmlFree(arg2);
-}
+    struct domnode *doc = node->owner ? node->owner : node;
+    const struct xpath_namespace *ns;
+    xmlChar *p, *uri;
 
-static void query_serror(void* ctx, const xmlError* err)
-{
-    LIBXML2_CALLBACK_SERROR(domselection_create, err);
+    LIST_FOR_EACH_ENTRY(ns, &doc->properties->namespaces.entries, struct xpath_namespace, entry)
+    {
+        if (*ns->prefix)
+        {
+            p = xmlchar_from_wchar(ns->prefix);
+            uri = xmlchar_from_wchar(ns->uri);
+            xmlXPathRegisterNs(ctxt, p, uri);
+            free(uri);
+            free(p);
+        }
+    }
 }
 
-static HRESULT select_nodes(struct domnode *node, BSTR query, bool xpath, struct selected_list *list)
+static HRESULT select_nodes(struct domnode *node, const WCHAR *query, bool xpath, struct selected_list *list)
 {
-    xmlXPathObjectPtr result;
-    xmlXPathContextPtr ctxt;
-    xmlNodePtr xmlnode;
-    xmlChar *xmlquery;
-    xmlDocPtr xmldoc;
+    struct xpath_object *result;
+    struct xpath_context *ctxt;
     HRESULT hr;
 
-    xmlquery = xmlchar_from_wchar(query);
-    xmldoc = create_xmldoc_from_domdoc(node, &xmlnode);
-
-    ctxt = xmlXPathNewContext(xmldoc);
-    ctxt->node = xmlnode;
-
-    ctxt->error = query_serror;
-    registerNamespaces(node, ctxt);
-    xmlXPathContextSetCache(ctxt, 1, -1, 0);
+    ctxt = xpath_create_context(xpath, node);
 
     if (xpath)
     {
-        xmlXPathRegisterAllFunctions(ctxt);
-        result = xmlXPathEvalExpression(xmlquery, ctxt);
+        result = xpath_eval(query, ctxt);
     }
     else
     {
-        xmlChar* pattern_query = XSLPattern_to_XPath(ctxt, xmlquery);
+        xmlChar *xmlquery, *pattern_query;
+        xmlXPathContextPtr _ctxt;
+        BSTR _query;
 
-        xmlXPathRegisterFunc(ctxt, (xmlChar const*)"not", xmlXPathNotFunction);
-        xmlXPathRegisterFunc(ctxt, (xmlChar const*)"boolean", xmlXPathBooleanFunction);
+        xmlquery = xmlchar_from_wchar(query);
+        _ctxt = xmlXPathNewContext(NULL);
+        xmlRegisterNamespaces(node, _ctxt);
 
-        xmlXPathRegisterFunc(ctxt, (xmlChar const*)"index", XSLPattern_index);
-        xmlXPathRegisterFunc(ctxt, (xmlChar const*)"end", XSLPattern_end);
-        xmlXPathRegisterFunc(ctxt, (xmlChar const*)"nodeType", XSLPattern_nodeType);
+        pattern_query = XSLPattern_to_XPath(_ctxt, xmlquery);
+        _query = bstr_from_xmlChar(pattern_query);
+        xmlFree(pattern_query);
 
-        xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IEq", XSLPattern_OP_IEq);
-        xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_INEq", XSLPattern_OP_INEq);
-        xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILt", XSLPattern_OP_ILt);
-        xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILEq", XSLPattern_OP_ILEq);
-        xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGt", XSLPattern_OP_IGt);
-        xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGEq", XSLPattern_OP_IGEq);
+        result = xpath_eval(_query, ctxt);
 
-        result = xmlXPathEvalExpression(pattern_query, ctxt);
-        xmlFree(pattern_query);
+        xmlXPathFreeContext(_ctxt);
+        SysFreeString(_query);
+        free(xmlquery);
     }
 
-    if (result && result->type == XPATH_NODESET)
+    if (result && result->type == _XPATH_NODESET)
     {
-        list->count = xmlXPathNodeSetGetLength(result->nodesetval);
+        list->count = result->nodesetval ? result->nodesetval->count : 0;
         list->nodes = calloc(list->count, sizeof(*list->nodes));
 
         for (int i = 0; i < list->count; ++i)
-        {
-            xmlnode = xmlXPathNodeSetItem(result->nodesetval, i);
-
-            if (xmlnode->type == XML_NAMESPACE_DECL)
-            {
-                FIXME("Not implemented for namespace nodes.\n");
-                list->count--;
-                continue;
-            }
-            else
-            {
-                node = xmlnode->_private2;
-            }
-
-            list->nodes[i] = domnode_addref(node);
-        }
+            list->nodes[i] = domnode_addref(result->nodesetval->nodes[i]);
 
         TRACE("found %ld matches\n", list->count);
         hr = S_OK;
@@ -785,10 +666,8 @@ static HRESULT select_nodes(struct domnode *node, BSTR query, bool xpath, struct
         hr = E_FAIL;
     }
 
-    xmlXPathFreeObject(result);
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xmldoc);
-    free(xmlquery);
+    xpath_free_object(result);
+    xpath_free_context(ctxt);
 
     return hr;
 }
diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c
index 39b70fd0300c..a33c4f5c6f26 100644
--- a/dlls/msxml3/tests/domdoc.c
+++ b/dlls/msxml3/tests/domdoc.c
@@ -5382,8 +5382,6 @@ static void test_XPath(void)
             IXMLDOMNodeList_Release(list);
     }
 
-if (!winetest_platform_is_wine)
-{
     /* namespace:: axis test is disabled until namespace definitions
        are supported as attribute nodes, currently it's another node type.
 
@@ -5446,7 +5444,6 @@ if (!winetest_platform_is_wine)
     IXMLDOMNode_Release(node);
 
     IXMLDOMNodeList_Release(list);
-}
 
     hr = IXMLDOMDocument2_selectNodes(doc, _bstr_("root//c"), &list);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
@@ -8753,7 +8750,6 @@ static void test_selectSingleNode(void)
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
     hr = IXMLDOMNode_get_nodeType(node, &type);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    todo_wine
     ok(type == NODE_ELEMENT, "Unexpected node %d.\n", type);
     IXMLDOMNode_Release(node);
 
@@ -8769,7 +8765,6 @@ static void test_selectSingleNode(void)
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
     hr = IXMLDOMNode_get_nodeType(node, &type);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-    todo_wine
     ok(type == NODE_ATTRIBUTE, "Unexpected node %d.\n", type);
     IXMLDOMNode_Release(node);
     hr = IXMLDOMAttribute_selectSingleNode(attr, _bstr_(".."), &node);
diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c
index 8a3d87401cb3..564ec69eb280 100644
--- a/dlls/msxml3/tests/saxreader.c
+++ b/dlls/msxml3/tests/saxreader.c
@@ -3417,10 +3417,15 @@ static void test_saxreader_encoding(void)
     static const char xml_shift_jis_test2[] =
         "<?xml version=\"1.0\" encoding=\"shift-jis\" ?><a>" "\x83\x89" "</a>";
 
+    static const char utf8_ws_test[] =
+        "    \r\n    <a>text</a>";
+
     const struct enc_test_entry_t *entry = encoding_test_data;
     static const CHAR testXmlA[] = "test.xml";
     DWORD ucs4_be_test[ARRAYSIZE(ucs4_le_test)];
     ISAXXMLReader *reader;
+    LARGE_INTEGER li = { 0 };
+    IStream* stream;
     HRESULT hr;
 
     for (int i = 0; i < ARRAYSIZE(ucs4_le_test); ++i)
@@ -3464,6 +3469,20 @@ static void test_saxreader_encoding(void)
         hr = ISAXXMLReader_parse(reader, input);
         ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
 
+        /* UTF-8 with leading white-spaces */
+        hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
+        ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+        hr = IStream_Write(stream, utf8_ws_test, sizeof(utf8_ws_test) - 1, NULL);
+        ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+        hr = IStream_Seek(stream, li, STREAM_SEEK_SET, NULL);
+        ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+        V_VT(&input) = VT_UNKNOWN;
+        V_UNKNOWN(&input) = (IUnknown*)stream;
+        hr = ISAXXMLReader_parse(reader, input);
+        ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+        VariantClear(&input);
+
         ISAXXMLReader_Release(reader);
 
         free_bstrs();
diff --git a/dlls/msxml3/xpath.c b/dlls/msxml3/xpath.c
new file mode 100644
index 000000000000..3f4e2e44634b
--- /dev/null
+++ b/dlls/msxml3/xpath.c
@@ -0,0 +1,6873 @@
+/*
+ * Copyright 2026 Nikolay Sivov 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
+ *
+ * Implementation is based on libxml2, copyright notice follows.
+ */
+
+/*
+ *  Copyright (C) 1998-2012 Daniel Veillard.  All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is fur-
+ * nished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+ * NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <math.h>
+
+#include "msxml6.h"
+#include "msxml_private.h"
+#include "xpath.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msxml);
+WINE_DECLARE_DEBUG_CHANNEL(msxml_xpath);
+
+enum xpath_global_config
+{
+    XPATH_MAX_RECURSION_DEPTH = 1000,
+    XPATH_MAX_FRAC = 20,
+
+    XPATH_DBL_DIG = 16,
+    XPATH_LOWER_DOUBLE_EXP = 5,
+    XPATH_INTEGER_DIGITS = XPATH_DBL_DIG,
+    XPATH_FRACTION_DIGITS = XPATH_DBL_DIG + 1 + XPATH_LOWER_DOUBLE_EXP,
+    XPATH_EXPONENT_DIGITS = 3 + 2,
+};
+
+static const double XPATH_UPPER_DOUBLE = 1e9;
+static const double XPATH_LOWER_DOUBLE = 1e-5;
+
+enum xpath_axis
+{
+    AXIS_UNDEFINED = 0,
+    AXIS_ANCESTOR,
+    AXIS_ANCESTOR_OR_SELF,
+    AXIS_ATTRIBUTE,
+    AXIS_CHILD,
+    AXIS_DESCENDANT,
+    AXIS_DESCENDANT_OR_SELF,
+    AXIS_FOLLOWING,
+    AXIS_FOLLOWING_SIBLING,
+    AXIS_NAMESPACE,
+    AXIS_PARENT,
+    AXIS_PRECEDING,
+    AXIS_PRECEDING_SIBLING,
+    AXIS_SELF,
+};
+
+enum xpath_test
+{
+    NODE_TEST_NONE = 0,
+    NODE_TEST_TYPE,
+    NODE_TEST_PI,
+    NODE_TEST_ALL,
+    NODE_TEST_NAME,
+};
+
+enum xpath_type
+{
+    NODE_TYPE_NODE = 0,
+    NODE_TYPE_COMMENT = NODE_COMMENT,
+    NODE_TYPE_TEXT = NODE_TEXT,
+    NODE_TYPE_PI = NODE_PROCESSING_INSTRUCTION,
+};
+
+struct xpath_parser_context;
+typedef void (*xpath_function_ptr)(struct xpath_parser_context *ctxt, int nargs);
+
+struct xpath_function
+{
+    WCHAR *name;
+    WCHAR *uri;
+    xpath_function_ptr func;
+};
+
+struct xpath_context
+{
+    struct domnode *root;
+    struct domnode *node; /* The current node */
+
+    struct
+    {
+        struct xpath_function *entries;
+        size_t count;
+        size_t capacity;
+    } functions;
+
+    bool xpath;
+
+    struct domdoc_properties *properties;
+
+    int contextSize;
+    int proximityPosition;
+
+    /* The function name and URI when calling a function */
+    const WCHAR *function;
+    const WCHAR *functionURI;
+
+    /* Resource limits */
+    unsigned long opLimit;
+    unsigned long opCount;
+    int depth;
+};
+
+enum xpath_op
+{
+    XPATH_OP_END = 0,
+    XPATH_OP_AND,
+    XPATH_OP_OR,
+    XPATH_OP_EQUAL,
+    XPATH_OP_CMP,
+    XPATH_OP_PLUS,
+    XPATH_OP_MULT,
+    XPATH_OP_UNION,
+    XPATH_OP_ROOT,
+    XPATH_OP_NODE,
+    XPATH_OP_COLLECT,
+    XPATH_OP_VALUE,
+    XPATH_OP_VARIABLE,
+    XPATH_OP_FUNCTION,
+    XPATH_OP_ARG,
+    XPATH_OP_PREDICATE,
+    XPATH_OP_FILTER,
+    XPATH_OP_SORT,
+};
+
+struct xpath_parser_context;
+typedef struct domnode * (*xpath_traversal_function)(struct xpath_parser_context *ctxt, struct domnode *cur);
+typedef struct xpath_nodeset * (*xpath_nodeset_merge_function)(struct xpath_parser_context *ctxt,
+        struct xpath_nodeset *set1, struct xpath_nodeset *set2);
+
+struct xpath_step_op
+{
+    enum xpath_op op;
+    int ch1;
+    int ch2;
+    int value;
+    int value2;
+    int value3;
+    void *value4;
+    void *value5;
+    xpath_function_ptr cache;
+    void *cacheURI;
+};
+
+struct xpath_comp_expr
+{
+    struct
+    {
+        size_t count;
+        size_t capacity;
+        struct xpath_step_op *values;
+    } steps;
+    int last; /* index of last step in expression */
+    WCHAR *expr; /* the expression being computed */
+};
+
+struct xpath_parser_context
+{
+    const WCHAR *cur;
+    const WCHAR *base;
+
+    int error; /* error code */
+
+    struct xpath_context *context; /* the evaluation context */
+    struct xpath_object *value; /* the current value */
+    struct
+    {
+        size_t count;
+        size_t capacity;
+        struct xpath_object **values;
+    } stack;
+
+    struct xpath_comp_expr *comp; /* the precompiled expression */
+    struct domnode *ancestor; /* used for walking preceding axis */
+
+    struct
+    {
+        size_t count;
+        size_t capacity;
+        struct domnode **nodes;
+    } namespaces;
+};
+
+/* In XPath data model attributes and namespace nodes have their containing
+   element as a parent. One exception for that is implicit "xmlns:xml" namespace which
+   is only exposed with namespace:: axis. For consistency we'll use XPath-specific
+   helper to account for that exception.
+
+   In DOM model such nodes have no parent, we handle that at IXMLDOMNode level. */
+static struct domnode * xpath_get_parent(const struct domnode *node)
+{
+    return (node->flags & DOMNODE_NO_PARENT) ? NULL : node->parent;
+}
+
+static int xpath_isinf(double val)
+{
+    return isinf(val) ? (val > 0 ? 1 : -1) : 0;
+}
+
+static void xpath_debug_indent(int depth)
+{
+    if (!depth) return;
+
+    while (--depth)
+        MESSAGE("  ");
+}
+
+static void xpath_debug_dump_nodeset(struct xpath_nodeset *cur, int depth)
+{
+    if (!cur)
+    {
+        xpath_debug_indent(depth);
+        MESSAGE("NodeSet is NULL !\n");
+        return;
+    }
+
+    MESSAGE("Set contains %d nodes.\n", (int)cur->count);
+}
+
+static void xpath_debug_dump_object(const struct xpath_object *cur, int depth)
+{
+    xpath_debug_indent(depth);
+
+    if (!cur)
+    {
+        MESSAGE("Object is empty (NULL)\n");
+        return;
+    }
+
+    switch (cur->type)
+    {
+        case XPATH_UNDEFINED:
+            MESSAGE("Object is uninitialized\n");
+            break;
+        case XPATH_NODESET:
+            MESSAGE("Object is a Node Set :\n");
+            xpath_debug_dump_nodeset(cur->nodesetval, depth);
+            break;
+        case XPATH_BOOLEAN:
+            MESSAGE("Object is a Boolean : %s\n", cur->boolval ? "true" : "false");
+            break;
+        case XPATH_NUMBER:
+            MESSAGE("Object is a number : ");
+            switch (xpath_isinf(cur->floatval))
+            {
+            case 1:
+                MESSAGE("Infinity\n");
+                break;
+            case -1:
+                MESSAGE("-Infinity\n");
+                break;
+            default:
+                if (isnan(cur->floatval))
+                    MESSAGE("NaN\n");
+                else if (cur->floatval == 0)
+                    /* Omit sign for negative zero. */
+                    MESSAGE("0\n");
+                else
+                    MESSAGE("%0g\n", cur->floatval);
+            }
+            break;
+        case XPATH_STRING:
+            MESSAGE("Object is a string : \"%s\"\n", debugstr_w(cur->stringval));
+            break;
+        default:
+            MESSAGE("Object is unknown\n");
+    }
+}
+
+static void xpath_debug_dump_step_op(const struct xpath_comp_expr *comp, int idx, const struct xpath_step_op *op, int depth)
+{
+    xpath_debug_indent(depth);
+    MESSAGE("%d ", idx);
+
+    if (!op)
+    {
+        MESSAGE("Step is NULL\n");
+        return;
+    }
+
+    switch (op->op)
+    {
+        case XPATH_OP_END:
+            MESSAGE("END"); break;
+        case XPATH_OP_AND:
+            MESSAGE("AND"); break;
+        case XPATH_OP_OR:
+            MESSAGE("OR"); break;
+        case XPATH_OP_EQUAL:
+            MESSAGE("EQUAL %s", op->value ? "=" : "!="); break;
+        case XPATH_OP_CMP:
+            MESSAGE("CMP %s", op->value ? "<" : ">");
+            if (!op->value2)
+                MESSAGE("=");
+            break;
+        case XPATH_OP_PLUS:
+            MESSAGE("PLUS ");
+            if (op->value == 0)
+                MESSAGE("-");
+            else if (op->value == 1)
+                MESSAGE("+");
+            else if (op->value == 2)
+                MESSAGE("unary -");
+            else if (op->value == 3)
+                MESSAGE("unary - -");
+            break;
+        case XPATH_OP_MULT:
+            MESSAGE("MULT ");
+            if (op->value == 0)
+                MESSAGE("*");
+            else if (op->value == 1)
+                MESSAGE("div");
+            else
+                MESSAGE("mod");
+            break;
+        case XPATH_OP_UNION:
+            MESSAGE("UNION"); break;
+        case XPATH_OP_ROOT:
+            MESSAGE("ROOT"); break;
+        case XPATH_OP_NODE:
+            MESSAGE("NODE"); break;
+        case XPATH_OP_SORT:
+            MESSAGE("SORT"); break;
+        case XPATH_OP_COLLECT:
+        {
+            enum xpath_axis axis = op->value;
+            enum xpath_test test = op->value2;
+            enum xpath_type type = op->value3;
+            const WCHAR *prefix = op->value4;
+            const WCHAR *name = op->value5;
+
+            MESSAGE("COLLECT");
+            switch (axis)
+            {
+                case AXIS_ANCESTOR:
+                    MESSAGE(" 'ancestor' "); break;
+                case AXIS_ANCESTOR_OR_SELF:
+                    MESSAGE(" 'ancestor-or-self' "); break;
+                case AXIS_ATTRIBUTE:
+                    MESSAGE(" 'attribute' "); break;
+                case AXIS_CHILD:
+                    MESSAGE(" 'child' "); break;
+                case AXIS_DESCENDANT:
+                    MESSAGE(" 'descendant' "); break;
+                case AXIS_DESCENDANT_OR_SELF:
+                    MESSAGE(" 'descendant-or-self' "); break;
+                case AXIS_FOLLOWING:
+                    MESSAGE(" 'following' "); break;
+                case AXIS_FOLLOWING_SIBLING:
+                    MESSAGE(" 'following-sibling' "); break;
+                case AXIS_NAMESPACE:
+                    MESSAGE(" 'namespace' "); break;
+                case AXIS_PARENT:
+                    MESSAGE(" 'parent' "); break;
+                case AXIS_PRECEDING:
+                    MESSAGE(" 'preceding' "); break;
+                case AXIS_PRECEDING_SIBLING:
+                    MESSAGE(" 'preceding-sibling' "); break;
+                case AXIS_SELF:
+                    MESSAGE(" 'self' "); break;
+                case AXIS_UNDEFINED:
+                    MESSAGE(" 'undefined' "); break;
+            }
+            switch (test)
+            {
+                case NODE_TEST_NONE:
+                    MESSAGE("'none' "); break;
+                case NODE_TEST_TYPE:
+                    MESSAGE("'type' "); break;
+                case NODE_TEST_PI:
+                    MESSAGE("'PI' "); break;
+                case NODE_TEST_ALL:
+                    MESSAGE("'all' "); break;
+                case NODE_TEST_NAME:
+                    MESSAGE("'name' "); break;
+            }
+            switch (type)
+            {
+                case NODE_TYPE_NODE:
+                    MESSAGE("'node' "); break;
+                case NODE_TYPE_COMMENT:
+                    MESSAGE("'comment' "); break;
+                case NODE_TYPE_TEXT:
+                    MESSAGE("'text' "); break;
+                case NODE_TYPE_PI:
+                    MESSAGE("'PI' "); break;
+            }
+            if (prefix)
+                MESSAGE("%s:", debugstr_w(prefix));
+            if (name)
+                MESSAGE("%s", debugstr_w(name));
+            break;
+        }
+        case XPATH_OP_VALUE:
+        {
+            struct xpath_object *object = op->value4;
+
+            MESSAGE("ELEM ");
+            xpath_debug_dump_object(object, 0);
+            goto finish;
+        }
+        case XPATH_OP_VARIABLE:
+        {
+            const WCHAR *prefix = op->value5;
+            const WCHAR *name = op->value4;
+
+            MESSAGE("VARIABLE ");
+            if (prefix)
+                MESSAGE("%s:", debugstr_w(prefix));
+            MESSAGE("%s", debugstr_w(name));
+            break;
+        }
+        case XPATH_OP_FUNCTION:
+        {
+            int nbargs = op->value;
+            const WCHAR *prefix = op->value5;
+            const WCHAR *name = op->value4;
+
+            MESSAGE("FUNCTION ");
+            if (prefix)
+                MESSAGE("%s:", debugstr_w(prefix));
+            MESSAGE("%s(%d args)", debugstr_w(name), nbargs);
+            break;
+        }
+        case XPATH_OP_ARG: MESSAGE("ARG"); break;
+        case XPATH_OP_PREDICATE: MESSAGE("PREDICATE"); break;
+        case XPATH_OP_FILTER: MESSAGE("FILTER"); break;
+        default:
+        MESSAGE("UNKNOWN %d\n", op->op); return;
+    }
+    MESSAGE("\n");
+finish:
+    if (op->ch1 >= 0)
+        xpath_debug_dump_step_op(comp, op->ch1, &comp->steps.values[op->ch1], depth + 1);
+    if (op->ch2 >= 0)
+        xpath_debug_dump_step_op(comp, op->ch2, &comp->steps.values[op->ch2], depth + 1);
+}
+
+static void xpath_debug_dump_comp_expr(const struct xpath_comp_expr *comp, int depth)
+{
+    if (!comp) return;
+
+    xpath_debug_indent(depth);
+    MESSAGE("Compiled Expression : %d elements\n", (int)comp->steps.count);
+    xpath_debug_dump_step_op(comp, comp->last, &comp->steps.values[comp->last], depth + 1);
+}
+
+typedef void (*xpath_function)(struct xpath_parser_context *ctxt, int nargs);
+
+static void xpath_parser_context_set_error(struct xpath_parser_context *ctxt, int error)
+{
+    if (ctxt->error == XPATH_EXPRESSION_OK)
+        ctxt->error = error;
+}
+
+static void * xpath_calloc(struct xpath_parser_context *ctxt, size_t count, size_t size)
+{
+    void *ret;
+
+    if (!(ret = calloc(count, size)))
+        xpath_parser_context_set_error(ctxt, XPATH_MEMORY_ERROR);
+
+    return ret;
+}
+
+static void * xpath_malloc(struct xpath_parser_context *ctxt, size_t size)
+{
+    void *ret;
+
+    if (!(ret = malloc(size)))
+        xpath_parser_context_set_error(ctxt, XPATH_MEMORY_ERROR);
+
+    return ret;
+}
+
+static bool xpath_array_reserve(struct xpath_parser_context *ctxt, void **elements, size_t *capacity, size_t count, size_t size)
+{
+    bool ret = array_reserve(elements, capacity, count, size);
+
+    if (!ret)
+        xpath_parser_context_set_error(ctxt, XPATH_MEMORY_ERROR);
+
+    return ret;
+}
+
+static WCHAR * xpath_strndup(struct xpath_parser_context *ctxt, const WCHAR *str, size_t len)
+{
+    WCHAR *ret = xpath_malloc(ctxt, (len + 1) * sizeof(WCHAR));
+
+    if (ret)
+    {
+        memcpy(ret, str, len * sizeof(WCHAR));
+        ret[len] = 0;
+    }
+
+    return ret;
+}
+
+static struct xpath_object * xpath_pop_value(struct xpath_parser_context *ctxt)
+{
+    struct xpath_object *ret;
+
+    if (!ctxt || ctxt->stack.count == 0)
+        return NULL;
+
+    ctxt->stack.count--;
+    if (ctxt->stack.count)
+        ctxt->value = ctxt->stack.values[ctxt->stack.count - 1];
+    else
+        ctxt->value = NULL;
+    ret = ctxt->stack.values[ctxt->stack.count];
+    ctxt->stack.values[ctxt->stack.count] = NULL;
+    return ret;
+}
+
+static void xpath_push_value(struct xpath_parser_context *ctxt, struct xpath_object *value)
+{
+    if (!value)
+        return xpath_parser_context_set_error(ctxt, XPATH_MEMORY_ERROR);
+
+    if (xpath_array_reserve(ctxt, (void **)&ctxt->stack.values, &ctxt->stack.capacity,
+            ctxt->stack.count + 1, sizeof(*ctxt->stack.values)))
+    {
+        ctxt->stack.values[ctxt->stack.count++] = value;
+        ctxt->value = value;
+    }
+}
+
+static struct xpath_comp_expr * xpath_create_comp_expr(struct xpath_parser_context *ctxt)
+{
+    struct xpath_comp_expr *cur;
+
+    if (!(cur = xpath_calloc(ctxt, 1, sizeof(*cur))))
+        return NULL;
+    cur->last = -1;
+    return cur;
+}
+
+static struct xpath_parser_context * xpath_create_parser_context(const WCHAR *str, struct xpath_context *ctxt)
+{
+    struct xpath_parser_context *ret;
+
+    if (!(ret = calloc(1, sizeof(*ret))))
+        return NULL;
+
+    ret->cur = ret->base = str;
+    ret->context = ctxt;
+    ret->comp = xpath_create_comp_expr(ret);
+
+    if (!ret->comp)
+    {
+        free(ret);
+        return NULL;
+    }
+
+    return ret;
+}
+
+static void xpath_free_nodeset(struct xpath_nodeset *val)
+{
+    if (!val) return;
+
+    free(val->nodes);
+    free(val);
+}
+
+void xpath_free_object(struct xpath_object *obj)
+{
+    if (!obj) return;
+
+    if (obj->type == _XPATH_NODESET)
+    {
+        xpath_free_nodeset(obj->nodesetval);
+    }
+    else if (obj->type == _XPATH_STRING)
+    {
+        free(obj->stringval);
+    }
+    free(obj);
+}
+
+static void xpath_object_release(struct xpath_context *ctxt, struct xpath_object *obj)
+{
+    xpath_free_object(obj);
+}
+
+static void xpath_free_comp_expr(struct xpath_comp_expr *comp)
+{
+    for (size_t i = 0; i < comp->steps.count; ++i)
+    {
+        struct xpath_step_op *op = &comp->steps.values[i];
+
+        if (op->value4)
+        {
+            if (op->op == XPATH_OP_VALUE)
+                xpath_free_object(op->value4);
+            else
+                free(op->value4);
+        }
+        free(op->value5);
+    }
+
+    free(comp->steps.values);
+    free(comp->expr);
+    free(comp);
+}
+
+static void xpath_free_parser_context(struct xpath_parser_context *ctxt)
+{
+    if (ctxt->stack.values)
+    {
+        for (int i = 0; i < ctxt->stack.count; ++i)
+        {
+            if (ctxt->context)
+                xpath_object_release(ctxt->context, ctxt->stack.values[i]);
+            else
+                xpath_free_object(ctxt->stack.values[i]);
+        }
+        free(ctxt->stack.values);
+    }
+
+    if (ctxt->comp)
+        xpath_free_comp_expr(ctxt->comp);
+    free(ctxt->namespaces.nodes);
+    free(ctxt);
+}
+
+static struct xpath_step_op * xpath_push_step(struct xpath_parser_context *ctxt)
+{
+    struct xpath_comp_expr *comp = ctxt->comp;
+    struct xpath_step_op *step;
+
+    if (!xpath_array_reserve(ctxt, (void **)&comp->steps.values, &comp->steps.capacity,
+            comp->steps.count + 1, sizeof(*comp->steps.values)))
+    {
+        return NULL;
+    }
+
+    comp->last = comp->steps.count;
+    step = &comp->steps.values[comp->steps.count++];
+    memset(step, 0, sizeof(*step));
+    return step;
+}
+
+static bool xpath_push_long_step(struct xpath_parser_context *ctxt, enum xpath_op op, int value, int value2, int value3,
+        void *value4, void *value5)
+{
+    int last = ctxt->comp->last;
+    struct xpath_step_op *step;
+
+    if (!(step = xpath_push_step(ctxt)))
+        return false;
+
+    step->op = op;
+    step->ch1 = last;
+    step->ch2 = -1;
+    step->value = value;
+    step->value2 = value2;
+    step->value3 = value3;
+    step->value4 = value4;
+    step->value5 = value5;
+
+    return true;
+}
+
+static bool xpath_push_full_step(struct xpath_parser_context *ctxt, enum xpath_op op, int ch1, int ch2,
+        int value, int value2, int value3, void *value4, void *value5)
+{
+    struct xpath_step_op *step = xpath_push_step(ctxt);
+
+    if (!step)
+        return false;
+
+    step->op = op;
+    step->ch1 = ch1;
+    step->ch2 = ch2;
+    step->value = value;
+    step->value2 = value2;
+    step->value3 = value3;
+    step->value4 = value4;
+    step->value5 = value5;
+
+    return true;
+}
+
+static void xpath_push_leave_step(struct xpath_parser_context *ctxt, enum xpath_op op, int value, int value2)
+{
+    struct xpath_step_op *step = xpath_push_step(ctxt);
+
+    step->op = op;
+    step->ch1 = -1;
+    step->ch2 = -1;
+    step->value = value;
+    step->value2 = value2;
+}
+
+static void xpath_push_unary_step(struct xpath_parser_context *ctxt, enum xpath_op op, int ch, int value, int value2)
+{
+    struct xpath_step_op *step = xpath_push_step(ctxt);
+
+    step->op = op;
+    step->ch1 = ch;
+    step->value = value;
+    step->value2 = value2;
+}
+
+static void xpath_push_binary_step(struct xpath_parser_context *ctxt, enum xpath_op op, int ch1, int ch2,
+        int value, int value2)
+{
+    struct xpath_step_op *step = xpath_push_step(ctxt);
+
+    step->op = op;
+    step->ch1 = ch1;
+    step->ch2 = ch2;
+    step->value = value;
+    step->value2 = value2;
+}
+
+static void xpath_parse_skipspaces(struct xpath_parser_context *ctxt)
+{
+    while (xml_is_space(*ctxt->cur))
+        ++ctxt->cur;
+}
+
+static void xpath_parse_skip(struct xpath_parser_context *ctxt, int count)
+{
+    ctxt->cur += count;
+}
+
+static void xpath_parse_next(struct xpath_parser_context *ctxt)
+{
+    xpath_parse_skip(ctxt, 1);
+}
+
+static WCHAR * xpath_parse_nc_name(struct xpath_parser_context *ctxt)
+{
+    const WCHAR *start = ctxt->cur;
+
+    if (!xml_is_ncname_startchar(*start))
+        return NULL;
+
+    do
+    {
+        xpath_parse_next(ctxt);
+    }
+    while (xml_is_ncnamechar(*ctxt->cur));
+
+    return xpath_strndup(ctxt, start, ctxt->cur - start);
+}
+
+struct xpath_qname
+{
+    WCHAR *prefix;
+    WCHAR *name;
+};
+
+static void xpath_free_qname(struct xpath_qname *name)
+{
+    free(name->prefix);
+    free(name->name);
+}
+
+static void xpath_parse_qname(struct xpath_parser_context *ctxt, struct xpath_qname *name)
+{
+    WCHAR *ret = NULL;
+
+    name->prefix = NULL;
+    ret = xpath_parse_nc_name(ctxt);
+    if (ret && *ctxt->cur == ':')
+    {
+        name->prefix = ret;
+        xpath_parse_next(ctxt);
+        ret = xpath_parse_nc_name(ctxt);
+    }
+
+    name->name = ret;
+}
+
+static bool is_ascii_digit(WCHAR c)
+{
+    return 0x30 <= c && c <= 0x39;
+}
+
+static bool is_ascii_letter(WCHAR c)
+{
+    return ((0x41 <= c) && (c <= 0x5a)) ||
+        ((0x61 <= c) && (c <= 0x7a));
+}
+
+/*
+ * [6] AxisName ::=   'ancestor'
+ *                  | 'ancestor-or-self'
+ *                  | 'attribute'
+ *                  | 'child'
+ *                  | 'descendant'
+ *                  | 'descendant-or-self'
+ *                  | 'following'
+ *                  | 'following-sibling'
+ *                  | 'namespace'
+ *                  | 'parent'
+ *                  | 'preceding'
+ *                  | 'preceding-sibling'
+ *                  | 'self'
+ */
+static enum xpath_axis xpath_is_axis_name(const WCHAR *name)
+{
+    if (!wcscmp(name, L"ancestor"))
+        return AXIS_ANCESTOR;
+    if (!wcscmp(name, L"ancestor-or-self"))
+        return AXIS_ANCESTOR_OR_SELF;
+    if (!wcscmp(name, L"attribute"))
+        return AXIS_ATTRIBUTE;
+    if (!wcscmp(name, L"child"))
+        return AXIS_CHILD;
+    if (!wcscmp(name, L"descendant"))
+        return AXIS_DESCENDANT;
+    if (!wcscmp(name, L"descendant-or-self"))
+        return AXIS_DESCENDANT_OR_SELF;
+    if (!wcscmp(name, L"following"))
+        return AXIS_FOLLOWING;
+    if (!wcscmp(name, L"following-sibling"))
+        return AXIS_FOLLOWING_SIBLING;
+    if (!wcscmp(name, L"namespace"))
+        return AXIS_NAMESPACE;
+    if (!wcscmp(name, L"parent"))
+        return AXIS_PARENT;
+    if (!wcscmp(name, L"preceding"))
+        return AXIS_PRECEDING;
+    if (!wcscmp(name, L"preceding-sibling"))
+        return AXIS_PRECEDING_SIBLING;
+    if (!wcscmp(name, L"self"))
+        return AXIS_SELF;
+
+    return AXIS_UNDEFINED;
+}
+
+static bool is_char(WCHAR c)
+{
+    return (((0x9 <= c) && (c <= 0xa)) || (c == 0xd) || (0x20 <= c));
+}
+
+static WCHAR * xpath_parse_literal(struct xpath_parser_context *ctxt)
+{
+    WCHAR quote = *ctxt->cur;
+    WCHAR *ret = NULL;
+    const WCHAR *s;
+
+    xpath_parse_next(ctxt);
+    s = ctxt->cur;
+
+    while (is_char(*ctxt->cur) && *ctxt->cur != quote)
+        xpath_parse_next(ctxt);
+
+    if (!is_char(*ctxt->cur))
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_UNFINISHED_LITERAL_ERROR);
+        return NULL;
+    }
+
+    ret = xpath_strndup(ctxt, s, ctxt->cur - s);
+    xpath_parse_next(ctxt);
+
+    return ret;
+}
+
+/*
+ * [7] NodeTest ::=   NameTest
+ *            | NodeType '(' ')'
+ *            | 'processing-instruction' '(' Literal ')'
+ *
+ * [37] NameTest ::=  '*'
+ *            | NCName ':' '*'
+ *            | QName
+ * [38] NodeType ::= 'comment'
+ *            | 'text'
+ *            | 'processing-instruction'
+ *            | 'node'
+ */
+static WCHAR * xpath_compile_nodetest(struct xpath_parser_context *ctxt, enum xpath_test *test,
+        enum xpath_type *type, WCHAR **prefix, WCHAR *name)
+{
+    bool blanks;
+
+    *type = 0;
+    *test = 0;
+    *prefix = NULL;
+    xpath_parse_skipspaces(ctxt);
+
+    if (!name && *ctxt->cur == '*')
+    {
+        xpath_parse_next(ctxt);
+        *test = NODE_TEST_ALL;
+        return NULL;
+    }
+
+    if (!name)
+        name = xpath_parse_nc_name(ctxt);
+    if (!name)
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_EXPR_ERROR);
+        return NULL;
+    }
+
+    blanks = xml_is_space(*ctxt->cur);
+    xpath_parse_skipspaces(ctxt);
+    if (*ctxt->cur == '(')
+    {
+        xpath_parse_next(ctxt);
+
+        /*
+         * NodeType or PI search
+         */
+        if (!wcscmp(name, L"comment"))
+            *type = NODE_TYPE_COMMENT;
+        else if (!wcscmp(name, L"node"))
+            *type = NODE_TYPE_NODE;
+        else if (!wcscmp(name, L"processing-instruction"))
+            *type = NODE_TYPE_PI;
+        else if (!wcscmp(name, L"text"))
+            *type = NODE_TYPE_TEXT;
+        else
+        {
+            free(name);
+            xpath_parser_context_set_error(ctxt, XPATH_EXPR_ERROR);
+            return NULL;
+        }
+
+        *test = NODE_TEST_TYPE;
+
+        xpath_parse_skipspaces(ctxt);
+        if (*type == NODE_TYPE_PI)
+        {
+            /*
+             * Specific case: search a PI by name.
+             */
+            free(name);
+            name = NULL;
+            if (*ctxt->cur != ')')
+            {
+                name = xpath_parse_literal(ctxt);
+                if (!name)
+                {
+                    xpath_parser_context_set_error(ctxt, XPATH_EXPR_ERROR);
+                    return NULL;
+                }
+                *test = NODE_TEST_PI;
+                xpath_parse_skipspaces(ctxt);
+            }
+        }
+        if (*ctxt->cur != ')')
+        {
+            free(name);
+            xpath_parser_context_set_error(ctxt, XPATH_UNCLOSED_ERROR);
+            return NULL;
+        }
+        xpath_parse_next(ctxt);
+        return name;
+    }
+
+    *test = NODE_TEST_NAME;
+    if (!blanks && *ctxt->cur == ':')
+    {
+        xpath_parse_next(ctxt);
+
+        *prefix = name;
+
+        if (*ctxt->cur == '*')
+        {
+            xpath_parse_next(ctxt);
+            *test = NODE_TEST_ALL;
+            return NULL;
+        }
+
+        name = xpath_parse_nc_name(ctxt);
+        if (!name)
+        {
+            xpath_parser_context_set_error(ctxt, XPATH_EXPR_ERROR);
+            return NULL;
+        }
+    }
+
+    return name;
+}
+
+static void xpath_compile_predicate(struct xpath_parser_context *ctxt, bool filter);
+
+/*
+ * [4] Step ::=   AxisSpecifier NodeTest Predicate*
+ *                  | AbbreviatedStep
+ *
+ * [12] AbbreviatedStep ::=   '.' | '..'
+ *
+ * [5] AxisSpecifier ::= AxisName '::'
+ *                  | AbbreviatedAxisSpecifier
+ *
+ * [13] AbbreviatedAxisSpecifier ::= '@'?
+ *
+ */
+static void xpath_compile_step(struct xpath_parser_context *ctxt)
+{
+    if (ctxt->error != XPATH_EXPRESSION_OK)
+        return;
+
+    xpath_parse_skipspaces(ctxt);
+    if (ctxt->cur[0] == '.' && ctxt->cur[1] == '.')
+    {
+        xpath_parse_skip(ctxt, 2);
+        xpath_parse_skipspaces(ctxt);
+        xpath_push_long_step(ctxt, XPATH_OP_COLLECT, AXIS_PARENT, NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
+    }
+    else if (ctxt->cur[0] == '.')
+    {
+        xpath_parse_next(ctxt);
+        xpath_parse_skipspaces(ctxt);
+    }
+    else
+    {
+        WCHAR *prefix = NULL, *name = NULL;
+        enum xpath_test test = 0;
+        enum xpath_axis axis = 0;
+        enum xpath_type type = 0;
+        int op1;
+
+        if (*ctxt->cur == '*')
+        {
+            axis = AXIS_CHILD;
+        }
+        else
+        {
+            name = xpath_parse_nc_name(ctxt);
+            if (name)
+            {
+                axis = xpath_is_axis_name(name);
+                if (axis)
+                {
+                    xpath_parse_skipspaces(ctxt);
+                    if (ctxt->cur[0] == ':' && ctxt->cur[1] == ':')
+                    {
+                        xpath_parse_skip(ctxt, 2);
+                        free(name);
+                        name = NULL;
+                    }
+                    else
+                    {
+                        axis = AXIS_CHILD;
+                    }
+                }
+                else
+                {
+                    axis = AXIS_CHILD;
+                }
+            }
+            else if (*ctxt->cur == '@')
+            {
+                xpath_parse_next(ctxt);
+                axis = AXIS_ATTRIBUTE;
+            }
+            else
+            {
+                axis = AXIS_CHILD;
+            }
+        }
+
+        if (ctxt->error != XPATH_EXPRESSION_OK)
+        {
+            free(name);
+            return;
+        }
+
+        name = xpath_compile_nodetest(ctxt, &test, &type, &prefix, name);
+        if (test == 0)
+            return;
+
+        op1 = ctxt->comp->last;
+        ctxt->comp->last = -1;
+
+        xpath_parse_skipspaces(ctxt);
+        while (*ctxt->cur == '[')
+            xpath_compile_predicate(ctxt, false);
+
+        if (!xpath_push_full_step(ctxt, XPATH_OP_COLLECT, op1, ctxt->comp->last, axis, test, type, prefix, name))
+        {
+            free(prefix);
+            free(name);
+        }
+    }
+}
+
+/*
+ *  [3]   RelativeLocationPath ::=   Step
+ *                     | RelativeLocationPath '/' Step
+ *                     | AbbreviatedRelativeLocationPath
+ *  [11]  AbbreviatedRelativeLocationPath ::=   RelativeLocationPath '//' Step
+ */
+static void xpath_compile_relative_location_path(struct xpath_parser_context *ctxt)
+{
+    xpath_parse_skipspaces(ctxt);
+    if (ctxt->cur[0] == '/' && ctxt->cur[1] == '/')
+    {
+        xpath_parse_skip(ctxt, 2);
+        xpath_parse_skipspaces(ctxt);
+        xpath_push_long_step(ctxt, XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, NODE_TEST_TYPE,
+                NODE_TYPE_NODE, NULL, NULL);
+    }
+    else if (ctxt->cur[0] == '/')
+    {
+        xpath_parse_next(ctxt);
+        xpath_parse_skipspaces(ctxt);
+    }
+    xpath_compile_step(ctxt);
+    if (ctxt->error != XPATH_EXPRESSION_OK)
+        return;
+
+    xpath_parse_skipspaces(ctxt);
+    while (ctxt->cur[0] == '/')
+    {
+        if (ctxt->cur[0] == '/' && ctxt->cur[1] == '/')
+        {
+            xpath_parse_skip(ctxt, 2);
+            xpath_parse_skipspaces(ctxt);
+            xpath_push_long_step(ctxt, XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, NODE_TEST_TYPE,
+                    NODE_TYPE_NODE, NULL, NULL);
+            xpath_compile_step(ctxt);
+        }
+        else if (ctxt->cur[0] == '/')
+        {
+            xpath_parse_next(ctxt);
+            xpath_parse_skipspaces(ctxt);
+            xpath_compile_step(ctxt);
+        }
+        xpath_parse_skipspaces(ctxt);
+    }
+}
+
+/*
+ *  [1]   LocationPath ::=   RelativeLocationPath
+ *                     | AbsoluteLocationPath
+ *  [2]   AbsoluteLocationPath ::=   '/' RelativeLocationPath?
+ *                     | AbbreviatedAbsoluteLocationPath
+ *  [10]   AbbreviatedAbsoluteLocationPath ::=
+ *                           '//' RelativeLocationPath
+ */
+static void xpath_compile_location_path(struct xpath_parser_context *ctxt)
+{
+    xpath_parse_skipspaces(ctxt);
+    if (*ctxt->cur != '/')
+    {
+        xpath_compile_relative_location_path(ctxt);
+    }
+    else
+    {
+        while (*ctxt->cur == '/')
+        {
+            if (ctxt->cur[0] == '/' && ctxt->cur[1] == '/')
+            {
+                xpath_parse_skip(ctxt, 2);
+                xpath_parse_skipspaces(ctxt);
+                xpath_push_long_step(ctxt, XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, NODE_TEST_TYPE,
+                        NODE_TYPE_NODE, NULL, NULL);
+                xpath_compile_relative_location_path(ctxt);
+            }
+            else if (ctxt->cur[0] == '/')
+            {
+                xpath_parse_next(ctxt);
+                xpath_parse_skipspaces(ctxt);
+                if (*ctxt->cur &&
+                        ((is_ascii_letter(*ctxt->cur)) || (*ctxt->cur >= 0x80) ||
+                        (*ctxt->cur == '_') || (*ctxt->cur == '.') ||
+                        (*ctxt->cur == '@') || (*ctxt->cur == '*')))
+                {
+                    xpath_compile_relative_location_path(ctxt);
+                }
+            }
+            if (ctxt->error != XPATH_EXPRESSION_OK)
+                return;
+        }
+    }
+}
+
+/*
+ *  [36]   VariableReference ::=   '$' QName
+ */
+static void xpath_compile_variable_reference(struct xpath_parser_context *ctxt)
+{
+    struct xpath_qname name;
+
+    if (ctxt->error != XPATH_EXPRESSION_OK)
+        return;
+
+    xpath_parse_skipspaces(ctxt);
+    if (*ctxt->cur != '$')
+        return xpath_parser_context_set_error(ctxt, XPATH_VARIABLE_REF_ERROR);
+
+    xpath_parse_next(ctxt);
+    xpath_parse_qname(ctxt, &name);
+    if (!name.name)
+    {
+        xpath_free_qname(&name);
+        return xpath_parser_context_set_error(ctxt, XPATH_VARIABLE_REF_ERROR);
+    }
+    ctxt->comp->last = -1;
+    if (!xpath_push_long_step(ctxt, XPATH_OP_VARIABLE, 0, 0, 0, name.name, name.prefix))
+        xpath_free_qname(&name);
+    xpath_parse_skipspaces(ctxt);
+}
+
+static struct xpath_object * xpath_wrap_nodeset(struct xpath_parser_context *ctxt, struct xpath_nodeset *val)
+{
+    struct xpath_object *ret;
+
+    if (!(ret = xpath_calloc(ctxt, 1, sizeof(*ret))))
+        return NULL;
+
+    ret->type = _XPATH_NODESET;
+    ret->nodesetval = val;
+
+    return ret;
+}
+
+static struct xpath_object *xpath_wrap_string(struct xpath_parser_context *ctxt, WCHAR *val)
+{
+    struct xpath_object *ret;
+
+    if (!(ret = xpath_calloc(ctxt, 1, sizeof(*ret))))
+        return NULL;
+
+    ret->type = _XPATH_STRING;
+    ret->stringval = val;
+
+    return ret;
+}
+
+static struct xpath_object * xpath_new_string(struct xpath_parser_context *ctxt, const WCHAR *val)
+{
+    struct xpath_object *ret;
+
+    if (!(ret = xpath_calloc(ctxt, 1, sizeof(*ret))))
+        return NULL;
+
+    ret->type = _XPATH_STRING;
+    if (val == NULL)
+        val = L"";
+    ret->stringval = wcsdup(val);
+    if (!ret->stringval)
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_MEMORY_ERROR);
+        free(ret);
+        return NULL;
+    }
+
+    return ret;
+}
+
+static struct xpath_nodeset * xpath_create_nodeset(struct xpath_parser_context *ctxt, struct domnode *val)
+{
+    struct xpath_nodeset *set;
+
+    if (!(set = xpath_calloc(ctxt, 1, sizeof(*set))))
+        return NULL;
+
+    if (val)
+    {
+        if (!xpath_array_reserve(ctxt, (void **)&set->nodes, &set->capacity, set->count + 1, sizeof(*set->nodes)))
+        {
+            free(set);
+            return NULL;
+        }
+
+        set->nodes[set->count++] = val;
+    }
+
+    return set;
+}
+
+static int xpath_nodeset_add_unique(struct xpath_parser_context *ctxt, struct xpath_nodeset *cur, struct domnode *val)
+{
+    if (!cur || !val)
+        return -1;
+
+    if (!xpath_array_reserve(ctxt, (void **)&cur->nodes, &cur->capacity, cur->count + 1, sizeof(*cur->nodes)))
+    {
+        return -1;
+    }
+
+    cur->nodes[cur->count++] = val;
+    return 0;
+}
+
+static struct xpath_nodeset * xpath_nodeset_merge(struct xpath_parser_context *ctxt, struct xpath_nodeset *val1,
+        struct xpath_nodeset *val2)
+{
+    int i, j, count, skip;
+    struct domnode *n1, *n2;
+
+    if (!val2)
+        return val1;
+    if (!val1)
+    {
+        if (!(val1 = xpath_create_nodeset(ctxt, NULL)))
+            return NULL;
+    }
+
+    count = val1->count;
+
+    for (i = 0; i < val2->count; ++i)
+    {
+        n2 = val2->nodes[i];
+        /*
+         * check against duplicates
+         */
+        skip = 0;
+        for (j = 0; j < count; j++)
+        {
+            n1 = val1->nodes[j];
+            if (n1 == n2)
+            {
+                skip = 1;
+                break;
+            }
+        }
+        if (skip)
+            continue;
+
+        xpath_nodeset_add_unique(ctxt, val1, n2);
+    }
+
+    return val1;
+}
+
+static struct xpath_object * xpath_new_nodeset(struct xpath_parser_context *ctxt, struct domnode *val)
+{
+    struct xpath_object *ret;
+
+    if (!(ret = xpath_calloc(ctxt, 1, sizeof(*ret))))
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_MEMORY_ERROR);
+        return NULL;
+    }
+    ret->type = _XPATH_NODESET;
+    ret->nodesetval = xpath_create_nodeset(ctxt, val);
+
+    return ret;
+}
+
+static struct xpath_object * xpath_new_number(struct xpath_parser_context *ctxt, double val)
+{
+    struct xpath_object *ret;
+
+    if (!(ret = xpath_calloc(ctxt, 1, sizeof(*ret))))
+        return NULL;
+    ret->type = _XPATH_NUMBER;
+    ret->floatval = val;
+
+    return ret;
+}
+
+static struct xpath_object * xpath_new_boolean(struct xpath_parser_context *ctxt, int val)
+{
+    struct xpath_object *ret;
+
+    if (!(ret = xpath_calloc(ctxt, 1, sizeof(*ret))))
+        return NULL;
+    ret->type = _XPATH_BOOLEAN;
+    ret->boolval = (val != 0);
+
+    return ret;
+}
+
+static struct xpath_object * xpath_object_copy(struct xpath_parser_context *ctxt, struct xpath_object *val)
+{
+    struct xpath_object *ret;
+
+    if (!val)
+        return NULL;
+
+    if (!(ret = xpath_calloc(ctxt, 1, sizeof(*ret))))
+        return NULL;
+
+    *ret = *val;
+    switch (val->type)
+    {
+        case XPATH_STRING:
+            ret->stringval = wcsdup(val->stringval);
+            if (!ret->stringval)
+            {
+                free(ret);
+                return NULL;
+            }
+            break;
+        case XPATH_NODESET:
+            ret->nodesetval = xpath_nodeset_merge(ctxt, NULL, val->nodesetval);
+            break;
+        default:
+            break;
+    }
+
+    return ret;
+}
+
+/**
+ *  [29]   Literal ::=   '"' [^"]* '"'
+ *                    | "'" [^']* "'"
+ */
+static void xpath_compile_literal(struct xpath_parser_context *ctxt)
+{
+    struct xpath_object *lit;
+    WCHAR *ret;
+
+    if (!(ret = xpath_parse_literal(ctxt)))
+        return;
+
+    lit = xpath_new_string(ctxt, ret);
+    if (!xpath_push_long_step(ctxt, XPATH_OP_VALUE, XPATH_STRING, 0, 0, lit, NULL))
+        xpath_object_release(ctxt->context, lit);
+
+    free(ret);
+}
+
+static void xpath_compile_expr(struct xpath_parser_context *ctxt, bool sort);
+
+/**
+ *  [30]   Number ::=   Digits ('.' Digits?)?
+ *                    | '.' Digits
+ *  [31]   Digits ::=   [0-9]+
+ */
+static void xpath_compile_number(struct xpath_parser_context *ctxt)
+{
+    bool ok = false, is_exponent_negative = false;
+    struct xpath_object *num;
+    double ret = 0.0;
+    int exponent = 0;
+
+    if (ctxt->error != XPATH_EXPRESSION_OK)
+        return;
+
+    if (ctxt->cur[0] != '.' && (ctxt->cur[0] < '0' || ctxt->cur[0] > '9'))
+        return xpath_parser_context_set_error(ctxt, XPATH_NUMBER_ERROR);
+
+    ret = 0;
+
+    while (is_ascii_digit(*ctxt->cur))
+    {
+        ret = ret * 10 + (*ctxt->cur - '0');
+        ok = true;
+        xpath_parse_next(ctxt);
+    }
+    if (*ctxt->cur == '.')
+    {
+        int v, frac = 0, max;
+        double fraction = 0;
+
+        xpath_parse_next(ctxt);
+        if (!is_ascii_digit(*ctxt->cur) && !ok)
+            return xpath_parser_context_set_error(ctxt, XPATH_NUMBER_ERROR);
+
+        while (*ctxt->cur == '0')
+        {
+            frac = frac + 1;
+            xpath_parse_next(ctxt);
+        }
+        max = frac + XPATH_MAX_FRAC;
+        while (is_ascii_digit(*ctxt->cur) && (frac < max))
+        {
+            v = (*ctxt->cur - '0');
+            fraction = fraction * 10 + v;
+            frac = frac + 1;
+            xpath_parse_next(ctxt);
+        }
+        fraction /= pow(10.0, frac);
+        ret = ret + fraction;
+        while (is_ascii_digit(*ctxt->cur))
+            xpath_parse_next(ctxt);
+    }
+    if (ctxt->cur[0] == 'e' || ctxt->cur[0] == 'E')
+    {
+        xpath_parse_next(ctxt);
+        if (*ctxt->cur == '-')
+        {
+            is_exponent_negative = true;
+            xpath_parse_next(ctxt);
+        }
+        else if (*ctxt->cur == '+')
+        {
+            xpath_parse_next(ctxt);
+        }
+        while (is_ascii_digit(*ctxt->cur))
+        {
+            if (exponent < 1000000)
+                exponent = exponent * 10 + (*ctxt->cur - '0');
+            xpath_parse_next(ctxt);
+        }
+        if (is_exponent_negative)
+            exponent = -exponent;
+        ret *= pow(10.0, (double) exponent);
+    }
+    num = xpath_new_number(ctxt, ret);
+    if (!xpath_push_long_step(ctxt, XPATH_OP_VALUE, _XPATH_NUMBER, 0, 0, num, NULL))
+    {
+        xpath_object_release(ctxt->context, num);
+    }
+}
+
+/**
+ *  [16]  FunctionCall ::=   FunctionName '(' ( Argument ( ',' Argument)*)? ')'
+ *  [17]  Argument ::=   Expr
+ */
+static void xpath_compile_function_call(struct xpath_parser_context *ctxt)
+{
+    struct xpath_qname name;
+    bool sort = true;
+    int nbargs = 0;
+
+    xpath_parse_qname(ctxt, &name);
+    if (!name.name)
+    {
+        xpath_free_qname(&name);
+        return xpath_parser_context_set_error(ctxt, XPATH_EXPR_ERROR);
+    }
+    xpath_parse_skipspaces(ctxt);
+
+    if (*ctxt->cur != '(')
+    {
+        xpath_free_qname(&name);
+        return xpath_parser_context_set_error(ctxt, XPATH_EXPR_ERROR);
+    }
+    xpath_parse_next(ctxt);
+    xpath_parse_skipspaces(ctxt);
+
+    /*
+    * Optimization for count(): we don't need the node-set to be sorted.
+    */
+    if (!name.prefix && !wcscmp(name.name, L"count"))
+    {
+        sort = false;
+    }
+    ctxt->comp->last = -1;
+    if (*ctxt->cur != ')')
+    {
+        while (*ctxt->cur)
+        {
+            int op1 = ctxt->comp->last;
+            ctxt->comp->last = -1;
+            xpath_compile_expr(ctxt, sort);
+            if (ctxt->error != XPATH_EXPRESSION_OK)
+            {
+                xpath_free_qname(&name);
+                return;
+            }
+            xpath_push_binary_step(ctxt, XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
+            nbargs++;
+            if (*ctxt->cur == ')') break;
+            if (*ctxt->cur != ',')
+            {
+                xpath_free_qname(&name);
+                return xpath_parser_context_set_error(ctxt, XPATH_EXPR_ERROR);
+            }
+            xpath_parse_next(ctxt);
+            xpath_parse_skipspaces(ctxt);
+        }
+    }
+
+    xpath_push_long_step(ctxt, XPATH_OP_FUNCTION, nbargs, 0, 0, name.name, name.prefix);
+    xpath_parse_next(ctxt);
+    xpath_parse_skipspaces(ctxt);
+}
+
+/*
+ *  [15]   PrimaryExpr ::=   VariableReference
+ *                | '(' Expr ')'
+ *                | Literal
+ *                | Number
+ *                | FunctionCall
+ */
+static void xpath_compile_primary_expr(struct xpath_parser_context *ctxt)
+{
+    xpath_parse_skipspaces(ctxt);
+    if (*ctxt->cur == '$')
+    {
+        xpath_compile_variable_reference(ctxt);
+    }
+    else if (*ctxt->cur == '(')
+    {
+        xpath_parse_next(ctxt);
+        xpath_parse_skipspaces(ctxt);
+        xpath_compile_expr(ctxt, true);
+        if (*ctxt->cur != ')')
+            return xpath_parser_context_set_error(ctxt, XPATH_EXPR_ERROR);
+        xpath_parse_next(ctxt);
+        xpath_parse_skipspaces(ctxt);
+    }
+    else if (is_ascii_digit(*ctxt->cur) || (*ctxt->cur == '.' && is_ascii_digit(ctxt->cur[1])))
+    {
+        xpath_compile_number(ctxt);
+    }
+    else if (*ctxt->cur == '\'' || *ctxt->cur == '"')
+    {
+        xpath_compile_literal(ctxt);
+    }
+    else
+    {
+        xpath_compile_function_call(ctxt);
+    }
+    xpath_parse_skipspaces(ctxt);
+}
+
+/*
+ *  [8] Predicate ::= '[' PredicateExpr ']'
+ *  [9] PredicateExpr ::= Expr
+ */
+static void xpath_compile_predicate(struct xpath_parser_context *ctxt, bool filter)
+{
+    int op1 = ctxt->comp->last;
+
+    xpath_parse_skipspaces(ctxt);
+    if (*ctxt->cur != '[')
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_PREDICATE_ERROR);
+    xpath_parse_next(ctxt);
+    xpath_parse_skipspaces(ctxt);
+
+    ctxt->comp->last = -1;
+    xpath_compile_expr(ctxt, filter);
+    if (ctxt->error != XPATH_EXPRESSION_OK)
+        return;
+
+    if (*ctxt->cur != ']')
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_PREDICATE_ERROR);
+
+    if (filter)
+        xpath_push_binary_step(ctxt, XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
+    else
+        xpath_push_binary_step(ctxt, XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
+
+    xpath_parse_next(ctxt);
+    xpath_parse_skipspaces(ctxt);
+}
+
+/**
+ *  [20]   FilterExpr ::=   PrimaryExpr
+ *               | FilterExpr Predicate
+ */
+static void xpath_compile_filter_expr(struct xpath_parser_context *ctxt)
+{
+    xpath_compile_primary_expr(ctxt);
+    xpath_parse_skipspaces(ctxt);
+
+    while (*ctxt->cur == '[')
+    {
+        xpath_compile_predicate(ctxt, true);
+        xpath_parse_skipspaces(ctxt);
+    }
+}
+
+/*
+ * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
+ *                  CombiningChar | Extender
+ *
+ * [5] Name ::= (Letter | '_' | ':') (NameChar)*
+ *
+ * [6] Names ::= Name (S Name)*
+ *
+ */
+static WCHAR * xpath_scan_name(struct xpath_parser_context *ctxt)
+{
+   const WCHAR *start = ctxt->cur;
+   int len = 0;
+
+   if (!xml_is_ncname_startchar(*ctxt->cur))
+       return NULL;
+
+   do
+   {
+       ++len;
+   }
+   while (xml_is_ncnamechar(ctxt->cur[len]));
+
+   return xpath_strndup(ctxt, start, len);
+}
+
+/**
+ *  [38]   NodeType ::=   'comment'
+ *                    | 'text'
+ *                    | 'processing-instruction'
+ *                    | 'node'
+ */
+static bool xpath_is_nodetype(const WCHAR *name)
+{
+    if (!wcscmp(name, L"node"))
+        return true;
+    if (!wcscmp(name, L"text"))
+        return true;
+    if (!wcscmp(name, L"comment"))
+        return true;
+    if (!wcscmp(name, L"processing-instruction"))
+        return true;
+
+    return false;
+}
+
+/*
+ *  [19]   PathExpr ::=   LocationPath
+ *               | FilterExpr
+ *               | FilterExpr '/' RelativeLocationPath
+ *               | FilterExpr '//' RelativeLocationPath
+ */
+static void xpath_compile_path_expr(struct xpath_parser_context *ctxt)
+{
+    bool location_path = true;
+    WCHAR *name = NULL;
+
+    xpath_parse_skipspaces(ctxt);
+    if (*ctxt->cur == '$' || *ctxt->cur == '(' ||
+        is_ascii_digit(*ctxt->cur) ||
+        (*ctxt->cur == '\'') || *ctxt->cur == '"' ||
+        (*ctxt->cur == '.' && is_ascii_digit(ctxt->cur[1])))
+    {
+        location_path = false;
+    }
+    else if ((*ctxt->cur == '*')
+            || (*ctxt->cur == '/')
+            || (*ctxt->cur == '@')
+            || (*ctxt->cur == '.'))
+    {
+        location_path = true;
+    }
+    else
+    {
+        /*
+         * Problem is finding if we have a name here whether it's:
+         *   - a nodetype
+         *   - a function call in which case it's followed by '('
+         *   - an axis in which case it's followed by ':'
+         *   - a element name
+         */
+        xpath_parse_skipspaces(ctxt);
+        name = xpath_scan_name(ctxt);
+        if (name && wcsstr(name, L"::"))
+        {
+            location_path = true;
+            free(name);
+        }
+        else if (name)
+        {
+            int len = wcslen(name);
+
+            while (ctxt->cur[len])
+            {
+                if (!xml_is_space(ctxt->cur[len]))
+                    break;
+                ++len;
+            }
+
+            location_path = true;
+            if (ctxt->cur[len++] == '(')
+                location_path = xpath_is_nodetype(name);
+
+            if (!ctxt->cur[len])
+                location_path = true;
+            free(name);
+        }
+    }
+
+    if (location_path)
+    {
+        if (*ctxt->cur == '/')
+            xpath_push_leave_step(ctxt, XPATH_OP_ROOT, 0, 0);
+        else
+            xpath_push_leave_step(ctxt, XPATH_OP_NODE, 0, 0);
+        xpath_compile_location_path(ctxt);
+    }
+    else
+    {
+        xpath_compile_filter_expr(ctxt);
+
+        if (ctxt->cur[0] == '/' && ctxt->cur[1] == '/')
+        {
+            xpath_parse_skip(ctxt, 2);
+            xpath_parse_skipspaces(ctxt);
+            xpath_push_long_step(ctxt, XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, NODE_TEST_TYPE,
+                    NODE_TYPE_NODE, NULL, NULL);
+
+            xpath_compile_relative_location_path(ctxt);
+        }
+        else if (ctxt->cur[0] == '/')
+        {
+            xpath_compile_relative_location_path(ctxt);
+        }
+    }
+    xpath_parse_skipspaces(ctxt);
+}
+
+/*
+ *  [18]   UnionExpr ::=   PathExpr
+ *               | UnionExpr '|' PathExpr
+ */
+static void xpath_compile_union_expr(struct xpath_parser_context *ctxt)
+{
+    xpath_compile_path_expr(ctxt);
+    xpath_parse_skipspaces(ctxt);
+    while (*ctxt->cur == '|')
+    {
+        int op1 = ctxt->comp->last;
+
+        xpath_push_leave_step(ctxt, XPATH_OP_NODE, 0, 0);
+
+        xpath_parse_next(ctxt);
+        xpath_parse_skipspaces(ctxt);
+        xpath_compile_path_expr(ctxt);
+
+        xpath_push_binary_step(ctxt, XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
+        xpath_parse_skipspaces(ctxt);
+    }
+}
+
+/*
+ *  [27]   UnaryExpr ::=   UnionExpr
+ *                   | '-' UnaryExpr
+ */
+static void xpath_compile_unary_expr(struct xpath_parser_context *ctxt)
+{
+    bool found = false;
+    int minus = 0;
+
+    xpath_parse_skipspaces(ctxt);
+    while (*ctxt->cur == '-')
+    {
+        minus = 1 - minus;
+        found = true;
+        xpath_parse_next(ctxt);
+        xpath_parse_skipspaces(ctxt);
+    }
+
+    xpath_compile_union_expr(ctxt);
+
+    if (found)
+    {
+        if (minus)
+            xpath_push_unary_step(ctxt, XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
+        else
+            xpath_push_unary_step(ctxt, XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
+    }
+}
+
+/*
+ *  [26]   MultiplicativeExpr ::=   UnaryExpr
+ *                   | MultiplicativeExpr MultiplyOperator UnaryExpr
+ *                   | MultiplicativeExpr 'div' UnaryExpr
+ *                   | MultiplicativeExpr 'mod' UnaryExpr
+ *  [34]   MultiplyOperator ::=   '*'
+ */
+static void xpath_compile_multiplicative_expr(struct xpath_parser_context *ctxt)
+{
+    xpath_compile_unary_expr(ctxt);
+    xpath_parse_skipspaces(ctxt);
+
+    while (*ctxt->cur == '*'
+            || (ctxt->cur[0] == 'd' && ctxt->cur[1] == 'i' && ctxt->cur[2] == 'v')
+            || (ctxt->cur[0] == 'm' && ctxt->cur[1] == 'o' && ctxt->cur[2] == 'd'))
+    {
+        int op = -1, op1 = ctxt->comp->last;
+
+        if (*ctxt->cur == '*')
+        {
+            op = 0;
+            xpath_parse_next(ctxt);
+        }
+        else if (*ctxt->cur == 'd')
+        {
+            op = 1;
+            xpath_parse_skip(ctxt, 3);
+        }
+        else if (*ctxt->cur == 'm')
+        {
+            op = 2;
+            xpath_parse_skip(ctxt, 3);
+        }
+        xpath_parse_skipspaces(ctxt);
+        xpath_compile_unary_expr(ctxt);
+        xpath_push_binary_step(ctxt, XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
+        xpath_parse_skipspaces(ctxt);
+    }
+}
+
+/*
+ *  [25]   AdditiveExpr ::=   MultiplicativeExpr
+ *                   | AdditiveExpr '+' MultiplicativeExpr
+ *                   | AdditiveExpr '-' MultiplicativeExpr
+ */
+static void xpath_compile_additive_expr(struct xpath_parser_context *ctxt)
+{
+    xpath_compile_multiplicative_expr(ctxt);
+    xpath_parse_skipspaces(ctxt);
+
+    while (ctxt->cur[0] == '+' || ctxt->cur[0] == '-')
+    {
+        int plus, op1 = ctxt->comp->last;
+
+        plus = ctxt->cur[0] == '+';
+        xpath_parse_next(ctxt);
+        xpath_parse_skipspaces(ctxt);
+        xpath_compile_multiplicative_expr(ctxt);
+        xpath_push_binary_step(ctxt, XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
+        xpath_parse_skipspaces(ctxt);
+    }
+}
+
+/*
+ *  [24]   RelationalExpr ::=   AdditiveExpr
+ *                 | RelationalExpr '<' AdditiveExpr
+ *                 | RelationalExpr '>' AdditiveExpr
+ *                 | RelationalExpr '<=' AdditiveExpr
+ *                 | RelationalExpr '>=' AdditiveExpr
+ */
+static void xpath_compiler_relational_expr(struct xpath_parser_context *ctxt)
+{
+    xpath_compile_additive_expr(ctxt);
+    xpath_parse_skipspaces(ctxt);
+
+    while (*ctxt->cur == '<' || *ctxt->cur == '>')
+    {
+        int op1 = ctxt->comp->last;
+        bool inf, strict;
+
+        inf = *ctxt->cur == '<';
+        strict = ctxt->cur[1] != '=';
+        xpath_parse_next(ctxt);
+        if (!strict)
+            xpath_parse_next(ctxt);
+        xpath_parse_skipspaces(ctxt);
+        xpath_compile_additive_expr(ctxt);
+        xpath_push_binary_step(ctxt, XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
+        xpath_parse_skipspaces(ctxt);
+    }
+}
+
+/*
+ *  [23]   EqualityExpr ::=   RelationalExpr
+ *                 | EqualityExpr '=' RelationalExpr
+ *                 | EqualityExpr '!=' RelationalExpr
+ */
+static void xpath_compile_equality_expr(struct xpath_parser_context *ctxt)
+{
+    xpath_compiler_relational_expr(ctxt);
+    xpath_parse_skipspaces(ctxt);
+
+    while (ctxt->cur[0] == '=' || (ctxt->cur[0] == '!' && ctxt->cur[1] == '='))
+    {
+        int op1 = ctxt->comp->last;
+        bool eq;
+
+        eq = *ctxt->cur == '=';
+        xpath_parse_next(ctxt);
+        if (!eq)
+            xpath_parse_next(ctxt);
+        xpath_parse_skipspaces(ctxt);
+        xpath_compiler_relational_expr(ctxt);
+        xpath_push_binary_step(ctxt, XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
+        xpath_parse_skipspaces(ctxt);
+    }
+}
+
+/*
+ *  [22]   AndExpr ::= EqualityExpr
+ *                 | AndExpr 'and' EqualityExpr
+ */
+static void xpath_compile_and_expr(struct xpath_parser_context *ctxt)
+{
+    xpath_compile_equality_expr(ctxt);
+    xpath_parse_skipspaces(ctxt);
+    while (ctxt->cur[0] == 'a' && ctxt->cur[1] == 'n' && ctxt->cur[2] == 'd')
+    {
+        int op1 = ctxt->comp->last;
+
+        xpath_parse_skip(ctxt, 3);
+        xpath_parse_skipspaces(ctxt);
+        xpath_compile_equality_expr(ctxt);
+        xpath_push_binary_step(ctxt, XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
+        xpath_parse_skipspaces(ctxt);
+    }
+}
+
+/*
+ *  [14]   Expr ::=   OrExpr
+ *  [21]   OrExpr ::=   AndExpr
+ *                 | OrExpr 'or' AndExpr
+ */
+static void xpath_compile_expr(struct xpath_parser_context *ctxt, bool sort)
+{
+    struct xpath_context *xpctxt = ctxt->context;
+
+    if (ctxt->error != XPATH_EXPRESSION_OK)
+        return;
+
+    if (xpctxt)
+    {
+        if (xpctxt->depth >= XPATH_MAX_RECURSION_DEPTH)
+            return xpath_parser_context_set_error(ctxt, XPATH_RECURSION_LIMIT_EXCEEDED);
+
+        xpctxt->depth += 10;
+    }
+
+    xpath_compile_and_expr(ctxt);
+    xpath_parse_skipspaces(ctxt);
+    while (ctxt->cur[0] == 'o' && ctxt->cur[1] == 'r')
+    {
+        int op1 = ctxt->comp->last;
+
+        xpath_parse_skip(ctxt, 2);
+        xpath_parse_skipspaces(ctxt);
+        xpath_compile_and_expr(ctxt);
+        xpath_push_binary_step(ctxt, XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
+        xpath_parse_skipspaces(ctxt);
+    }
+
+    if (sort && (ctxt->comp->steps.values[ctxt->comp->last].op != XPATH_OP_VALUE))
+    {
+        /* more ops could be optimized too */
+        /*
+        * This is the main place to eliminate sorting for
+        * operations which don't require a sorted node-set.
+        * E.g. count().
+        */
+        xpath_push_unary_step(ctxt, XPATH_OP_SORT, ctxt->comp->last, 0, 0);
+    }
+
+    if (xpctxt)
+        xpctxt->depth -= 10;
+}
+
+static int xpath_cmp_nodes(struct domnode *node1, struct domnode *node2)
+{
+    struct domnode *attrNode1 = NULL, *attrNode2 = NULL;
+    bool attr1 = false, attr2 = false;
+    struct domnode *cur, *root;
+    int depth1, depth2;
+
+    if (!node1 || !node2)
+        return -2;
+
+    if (node1 == node2)
+        return 0;
+
+    if (domnode_is_namespace_declaration(node1)
+            || domnode_is_namespace_declaration(node2))
+    {
+        return 1;
+    }
+
+    if (node1->type == NODE_ATTRIBUTE)
+    {
+        attr1 = true;
+        attrNode1 = node1;
+        node1 = xpath_get_parent(node1);
+    }
+
+    if (node2->type == NODE_ATTRIBUTE)
+    {
+        attr2 = true;
+        attrNode2 = node2;
+        node2 = xpath_get_parent(node2);
+    }
+
+    if (node1 == node2)
+    {
+        if (attr1 == attr2)
+        {
+            /* Attributes are kept in order */
+            if (attr1)
+            {
+                cur = domnode_get_previous_sibling(attrNode2);
+                while (cur)
+                {
+                    if (cur == attrNode1)
+                        return 1;
+                    cur = domnode_get_previous_sibling(cur);
+                }
+                return -1;
+            }
+            return 0;
+        }
+
+        if (attr2)
+            return 1;
+
+        return -1;
+    }
+
+    if (node1 == domnode_get_previous_sibling(node2))
+        return 1;
+    if (node1 == domnode_get_next_sibling(node2))
+        return -1;
+
+    /*
+     * compute depth to root
+     */
+    for (depth2 = 0, cur = node2; xpath_get_parent(cur); cur = xpath_get_parent(cur))
+    {
+        if (xpath_get_parent(cur) == node1)
+            return 1;
+        depth2++;
+    }
+
+    root = cur;
+    for (depth1 = 0, cur = node1; xpath_get_parent(cur); cur = xpath_get_parent(cur))
+    {
+        if (xpath_get_parent(cur) == node2)
+            return -1;
+        depth1++;
+    }
+
+    if (root != cur)
+        return -2;
+
+    /*
+     * get the nearest common ancestor.
+     */
+    while (depth1 > depth2)
+    {
+        depth1--;
+        node1 = xpath_get_parent(node1);
+    }
+
+    while (depth2 > depth1)
+    {
+        depth2--;
+        node2 = xpath_get_parent(node2);
+    }
+
+    while (xpath_get_parent(node1) != xpath_get_parent(node2))
+    {
+        node1 = xpath_get_parent(node1);
+        node2 = xpath_get_parent(node2);
+        if (!node1 || !node2)
+            return -2;
+    }
+    /*
+     * Find who's first.
+     */
+    if (node1 == domnode_get_previous_sibling(node2))
+        return 1;
+    if (node1 == domnode_get_next_sibling(node2))
+        return -1;
+
+    for (cur = domnode_get_next_sibling(node1); cur; cur = domnode_get_next_sibling(cur))
+    {
+        if (cur == node2)
+            return 1;
+    }
+
+    return -1;
+}
+
+static void xpath_nodeset_sort(struct xpath_nodeset *set)
+{
+    int i, j, incr, len;
+    struct domnode *tmp;
+
+    if (!set)
+        return;
+
+    len = set->count;
+    for (incr = len / 2; incr > 0; incr /= 2)
+    {
+        for (i = incr; i < len; i++)
+        {
+            j = i - incr;
+            while (j >= 0)
+            {
+                if (xpath_cmp_nodes(set->nodes[j], set->nodes[j + incr]) != -1)
+                    break;
+
+                tmp = set->nodes[j];
+                set->nodes[j] = set->nodes[j + incr];
+                set->nodes[j + incr] = tmp;
+                j -= incr;
+            }
+        }
+    }
+}
+
+static int xpath_cast_string_to_boolean(const WCHAR *val)
+{
+    return val && *val;
+}
+
+static int xpath_cast_number_to_boolean(double val)
+{
+    return !isnan(val) && val != 0.0;
+}
+
+static int xpath_cast_nodeset_to_boolean(struct xpath_nodeset *set)
+{
+    return set && set->count;
+}
+
+static WCHAR * xpath_cast_node_to_string(struct domnode *node)
+{
+    WCHAR *ret = NULL;
+    BSTR str;
+
+    if (node_get_text(node, &str) == S_OK)
+    {
+        ret = wcsdup(str);
+        SysFreeString(str);
+    }
+
+    return ret;
+}
+
+static WCHAR * xpath_cast_nodeset_to_string(struct xpath_nodeset *set)
+{
+    if (!set || !set->count || !set->nodes)
+        return wcsdup(L"");
+
+    xpath_nodeset_sort(set);
+    return xpath_cast_node_to_string(set->nodes[0]);
+}
+
+static double xpath_cast_string_to_number(const WCHAR *val);
+
+static double xpath_cast_nodeset_to_number(struct xpath_nodeset *set)
+{
+    WCHAR *str;
+    double ret;
+
+    if (!set)
+        return NAN;
+
+    str = xpath_cast_nodeset_to_string(set);
+    ret = xpath_cast_string_to_number(str);
+    free(str);
+    return ret;
+}
+
+static int xpath_cast_to_boolean(struct xpath_object *val)
+{
+    if (!val)
+        return(0);
+
+    switch (val->type)
+    {
+    case XPATH_NODESET:
+        return xpath_cast_nodeset_to_boolean(val->nodesetval);
+    case XPATH_STRING:
+        return xpath_cast_string_to_boolean(val->stringval);
+    case XPATH_NUMBER:
+        return xpath_cast_number_to_boolean(val->floatval);
+    case XPATH_BOOLEAN:
+        return val->boolval;
+    default:
+        return 0;
+    }
+}
+
+static struct xpath_object * xpath_convert_boolean(struct xpath_parser_context *ctxt,
+        struct xpath_object *val)
+{
+    struct xpath_object *ret;
+
+    if (!val)
+        return xpath_new_boolean(ctxt, 0);
+
+    if (val->type == _XPATH_BOOLEAN)
+        return val;
+
+    ret = xpath_new_boolean(ctxt, xpath_cast_to_boolean(val));
+    xpath_free_object(val);
+    return ret;
+}
+
+static bool xpath_builtin_check_stack(struct xpath_parser_context *ctxt,
+        int nargs, int count)
+{
+    if (nargs != count)
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_INVALID_ARITY);
+        return false;
+    }
+
+    if (ctxt->stack.count < count)
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_STACK_ERROR);
+        return false;
+    }
+
+    return true;
+}
+
+static void xpath_builtin_boolean(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *cur;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+
+    cur = xpath_pop_value(ctxt);
+    if (!cur)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+
+    cur = xpath_convert_boolean(ctxt, cur);
+    xpath_push_value(ctxt, cur);
+}
+
+static void xpath_builtin_number(struct xpath_parser_context *ctxt, int nargs);
+
+static void xpath_arg_cast_to_number(struct xpath_parser_context *ctxt)
+{
+    if (ctxt->value && ctxt->value->type != _XPATH_NUMBER)
+        xpath_builtin_number(ctxt, 1);
+}
+
+static bool xpath_check_type(struct xpath_parser_context *ctxt, int type)
+{
+    if (!ctxt->value || ctxt->value->type != type)
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+        return false;
+    }
+
+    return true;
+}
+
+static void xpath_value_flip_sign(struct xpath_parser_context *ctxt)
+{
+    if (!ctxt || !ctxt->context)
+        return;
+
+    xpath_arg_cast_to_number(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+        return;
+    ctxt->value->floatval = -ctxt->value->floatval;
+}
+
+static double xpath_cast_to_number(struct xpath_object *val);
+
+static void xpath_add_values(struct xpath_parser_context *ctxt)
+{
+    struct xpath_object *arg;
+    double val;
+
+    arg = xpath_pop_value(ctxt);
+    if (arg == NULL)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+
+    val = xpath_cast_to_number(arg);
+    xpath_object_release(ctxt->context, arg);
+    xpath_arg_cast_to_number(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+        return;
+
+    ctxt->value->floatval += val;
+}
+
+static void xpath_sub_values(struct xpath_parser_context *ctxt)
+{
+    struct xpath_object *arg;
+    double val;
+
+    arg = xpath_pop_value(ctxt);
+    if (arg == NULL)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+
+    val = xpath_cast_to_number(arg);
+    xpath_object_release(ctxt->context, arg);
+    xpath_arg_cast_to_number(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+        return;
+
+    ctxt->value->floatval -= val;
+}
+
+static void xpath_mult_values(struct xpath_parser_context *ctxt)
+{
+    struct xpath_object *arg;
+    double val;
+
+    arg = xpath_pop_value(ctxt);
+    if (arg == NULL)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+
+    val = xpath_cast_to_number(arg);
+    xpath_object_release(ctxt->context, arg);
+    xpath_arg_cast_to_number(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+        return;
+
+    ctxt->value->floatval *= val;
+}
+
+static void xpath_div_values(struct xpath_parser_context *ctxt)
+{
+    struct xpath_object *arg;
+    double val;
+
+    arg = xpath_pop_value(ctxt);
+    if (arg == NULL)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+
+    val = xpath_cast_to_number(arg);
+    xpath_object_release(ctxt->context, arg);
+    xpath_arg_cast_to_number(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+        return;
+
+    ctxt->value->floatval /= val;
+}
+
+static void xpath_mod_values(struct xpath_parser_context *ctxt)
+{
+    struct xpath_object *arg;
+    double val, arg1;
+
+    arg = xpath_pop_value(ctxt);
+    if (arg == NULL)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+
+    val = xpath_cast_to_number(arg);
+    xpath_object_release(ctxt->context, arg);
+    xpath_arg_cast_to_number(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+        return;
+
+    arg1 = ctxt->value->floatval;
+    if (val == 0)
+        ctxt->value->floatval = NAN;
+    else
+        ctxt->value->floatval = fmod(arg1, val);
+}
+
+static int xpath_check_op_limit(struct xpath_parser_context *ctxt, unsigned long count)
+{
+    struct xpath_context *xpctxt = ctxt->context;
+
+    if ((count > xpctxt->opLimit) || (xpctxt->opCount > xpctxt->opLimit - count))
+    {
+        xpctxt->opCount = xpctxt->opLimit;
+        xpath_parser_context_set_error(ctxt, XPATH_OP_LIMIT_EXCEEDED);
+        return -1;
+    }
+
+    xpctxt->opCount += count;
+    return 0;
+}
+
+static int xpath_equal_number_values(double arg1, double arg2)
+{
+    int ret;
+
+    if (isnan(arg1) || isnan(arg2))
+        ret = 0;
+    else if (xpath_isinf(arg1) == 1)
+        ret = (xpath_isinf(arg2) == 1);
+    else if (xpath_isinf(arg1) == -1)
+        ret = (xpath_isinf(arg2) == -1);
+    else if (xpath_isinf(arg2) == 1)
+        ret = (xpath_isinf(arg1) == 1);
+    else if (xpath_isinf(arg2) == -1)
+        ret = (xpath_isinf(arg1) == -1);
+    else
+        ret = arg1 == arg2;
+
+    return ret;
+}
+
+static bool xpath_equal_values_common(struct xpath_parser_context *ctxt,
+        struct xpath_object *arg1, struct xpath_object *arg2)
+{
+    bool ret = false;
+
+    switch (arg1->type)
+    {
+        case XPATH_BOOLEAN:
+            switch (arg2->type)
+            {
+                case XPATH_BOOLEAN:
+                    ret = arg1->boolval == arg2->boolval;
+                    break;
+                case XPATH_NUMBER:
+                    ret = arg1->boolval == xpath_cast_number_to_boolean(arg2->floatval);
+                    break;
+                case XPATH_STRING:
+                    ret = arg2->stringval && arg2->stringval[0];
+                    ret = (arg1->boolval == ret);
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case XPATH_NUMBER:
+            switch (arg2->type)
+            {
+                case XPATH_BOOLEAN:
+                    ret = arg2->boolval == xpath_cast_number_to_boolean(arg1->floatval);
+                    break;
+                case XPATH_STRING:
+                    xpath_push_value(ctxt, arg2);
+                    xpath_builtin_number(ctxt, 1);
+                    arg2 = xpath_pop_value(ctxt);
+                    if (ctxt->error)
+                        break;
+
+                    /* Falls through. */
+                case XPATH_NUMBER:
+                    ret = xpath_equal_number_values(arg1->floatval, arg2->floatval);
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case XPATH_STRING:
+            switch (arg2->type)
+            {
+                case XPATH_BOOLEAN:
+                    ret = arg1->stringval && arg1->stringval[0];
+                    ret = (arg2->boolval == ret);
+                    break;
+                case XPATH_STRING:
+                    ret = !wcscmp(arg1->stringval, arg2->stringval);
+                    break;
+                case XPATH_NUMBER:
+                    xpath_push_value(ctxt, arg1);
+                    xpath_builtin_number(ctxt, 1);
+                    arg1 = xpath_pop_value(ctxt);
+                    if (ctxt->error)
+                        break;
+
+                    ret = xpath_equal_number_values(arg1->floatval, arg2->floatval);
+                    break;
+                default:
+                    break;
+            }
+            break;
+        default:
+            break;
+    }
+
+    xpath_object_release(ctxt->context, arg1);
+    xpath_object_release(ctxt->context, arg2);
+    return ret;
+}
+
+static unsigned int xpath_string_hash(const WCHAR *s)
+{
+    if (!s || !*s)
+        return 0;
+    return s[0] + (s[1] << 16);
+}
+
+static unsigned int xpath_nodeval_hash(struct domnode *node)
+{
+    struct domnode *tmp = NULL, *next;
+    const WCHAR *string = NULL;
+    unsigned int ret = 0;
+    int len = 2;
+
+    if (!node)
+        return 0;
+
+    if (node->type == NODE_DOCUMENT)
+    {
+        tmp = domnode_get_root_element(node);
+        node = tmp ? tmp : domnode_get_first_child(node);
+
+        if (!node)
+            return 0;
+    }
+
+    switch (node->type)
+    {
+        case NODE_COMMENT:
+        case NODE_PROCESSING_INSTRUCTION:
+        case NODE_CDATA_SECTION:
+        case NODE_TEXT:
+            return xpath_string_hash(node->data);
+        case NODE_ATTRIBUTE:
+        case NODE_ELEMENT:
+            tmp = domnode_get_first_child(node);
+            break;
+        default:
+            return 0;
+    }
+
+    while (tmp)
+    {
+        switch (tmp->type)
+        {
+            case NODE_CDATA_SECTION:
+            case NODE_TEXT:
+                string = tmp->data;
+                break;
+            default:
+                string = NULL;
+                break;
+        }
+
+        if (string && *string)
+        {
+            if (len == 1)
+                return ret + (string[0] << 16);
+            if (!string[1])
+            {
+                len = 1;
+                ret = string[0];
+            }
+            else
+            {
+                return xpath_string_hash(string);
+            }
+        }
+
+        /*
+         * Skip to next node
+         */
+        if (!list_empty(&tmp->children) &&
+            (tmp->type != NODE_DOCUMENT_TYPE) &&
+            (tmp->type != NODE_ENTITY_REFERENCE) &&
+            (domnode_get_first_child(tmp)->type != NODE_ENTITY))
+        {
+            tmp = domnode_get_first_child(tmp);
+            continue;
+        }
+        if (tmp == node)
+            break;
+
+        next = domnode_get_next_sibling(tmp);
+        if (next)
+        {
+            tmp = next;
+            continue;
+        }
+
+        do
+        {
+            tmp = xpath_get_parent(tmp);
+            if (!tmp)
+                break;
+            if (tmp == node)
+            {
+                tmp = NULL;
+                break;
+            }
+            next = domnode_get_next_sibling(tmp);
+            if (next)
+            {
+                tmp = next;
+                break;
+            }
+        } while (tmp);
+    }
+
+    return ret;
+}
+
+static bool xpath_equal_nodesets(struct xpath_parser_context *ctxt, struct xpath_object *arg1,
+        struct xpath_object *arg2, int neq)
+{
+    struct xpath_nodeset *ns1, *ns2;
+    unsigned int *hashs1;
+    unsigned int *hashs2;
+    bool ret = false;
+    WCHAR **values1;
+    WCHAR **values2;
+    int i, j;
+
+    if (!arg1 || arg1->type != _XPATH_NODESET)
+        return false;
+    if (!arg2 || arg2->type != _XPATH_NODESET)
+        return false;
+
+    ns1 = arg1->nodesetval;
+    ns2 = arg2->nodesetval;
+
+    if (!ns1 || ns1->count <= 0)
+        return false;
+    if (!ns2 || ns2->count <= 0)
+        return false;
+
+    if (neq == 0)
+    {
+        for (i = 0; i < ns1->count; ++i)
+        {
+            for (j = 0; j < ns2->count; ++j)
+            {
+                if (ns1->nodes[i] == ns2->nodes[j])
+                    return true;
+            }
+        }
+    }
+
+    if (!(values1 = xpath_calloc(ctxt, ns1->count, sizeof(*values1))))
+        return false;
+    if (!(hashs1 = xpath_malloc(ctxt, ns1->count * sizeof(*hashs1))))
+    {
+        free(values1);
+        return false;
+    }
+
+    if (!(values2 = xpath_calloc(ctxt, ns2->count, sizeof(*values2))))
+    {
+        free(values1);
+        free(hashs1);
+        return false;
+    }
+    if (!(hashs2 = xpath_malloc(ctxt, ns2->count * sizeof(*hashs2))))
+    {
+        free(values1);
+        free(values2);
+        free(hashs1);
+        return false;
+    }
+
+    for (i = 0; i < ns1->count; ++i)
+    {
+        hashs1[i] = xpath_nodeval_hash(ns1->nodes[i]);
+        for (j = 0; j < ns2->count; ++j)
+        {
+            if (i == 0)
+                hashs2[j] = xpath_nodeval_hash(ns2->nodes[j]);
+            if (hashs1[i] != hashs2[j])
+            {
+                if (neq)
+                {
+                    ret = true;
+                    break;
+                }
+            }
+            else
+            {
+                if (!values1[i])
+                    values1[i] = xpath_cast_node_to_string(ns1->nodes[i]);
+                if (!values2[j])
+                    values2[j] = xpath_cast_node_to_string(ns2->nodes[j]);
+
+                ret = ((values1[i] && values2[j]) ? !wcscmp(values1[i], values2[j]) : 0) ^ neq;
+                if (ret)
+                    break;
+            }
+        }
+        if (ret)
+            break;
+    }
+
+    for (i = 0; i < ns1->count; ++i)
+        free(values1[i]);
+    for (i = 0; i < ns2->count; ++i)
+        free(values2[i]);
+
+    free(values1);
+    free(values2);
+    free(hashs1);
+    free(hashs2);
+    return ret;
+}
+
+static bool xpath_equal_nodeset_float(struct xpath_parser_context *ctxt,
+        struct xpath_object *arg, double f, bool neq)
+{
+    struct xpath_nodeset *ns;
+    struct xpath_object *val;
+    bool ret = false;
+    WCHAR *str2;
+    double v;
+    int i;
+
+    if (!arg || ((arg->type != _XPATH_NODESET)))
+        return false;
+
+    ns = arg->nodesetval;
+    if (ns)
+    {
+        for (i = 0; i < ns->count; ++i)
+        {
+            str2 = xpath_cast_node_to_string(ns->nodes[i]);
+            if (str2)
+            {
+                xpath_push_value(ctxt, xpath_new_string(ctxt, str2));
+                free(str2);
+                xpath_builtin_number(ctxt, 1);
+                if (ctxt->error != XPATH_EXPRESSION_OK) return false;
+                val = xpath_pop_value(ctxt);
+                v = val->floatval;
+                xpath_object_release(ctxt->context, val);
+                if (!isnan(v))
+                {
+                    if (!neq && (v == f))
+                    {
+                        ret = true;
+                        break;
+                    }
+                    else if (neq && (v != f))
+                    {
+                        ret = true;
+                        break;
+                    }
+                }
+                else
+                {
+                    if (neq)
+                        ret = true;
+                }
+            }
+        }
+    }
+
+    return ret;
+}
+
+static bool xpath_equal_nodeset_string(struct xpath_object *arg, const WCHAR *str, bool neq)
+{
+    struct xpath_nodeset *ns;
+    unsigned int hash;
+    WCHAR *str2;
+    int i;
+
+    if (!str || !arg || arg->type != _XPATH_NODESET)
+        return false;
+    ns = arg->nodesetval;
+
+    if (!ns || ns->count <= 0)
+        return false;
+    hash = xpath_string_hash(str);
+    for (i = 0; i < ns->count; ++i)
+    {
+        if (xpath_nodeval_hash(ns->nodes[i]) == hash)
+        {
+            str2 = xpath_cast_node_to_string(ns->nodes[i]);
+            if (str2 && !wcscmp(str, str2))
+            {
+                free(str2);
+                if (neq)
+                    continue;
+                return true;
+            }
+            else if (!str2 && !*str)
+            {
+                if (neq)
+                    continue;
+                return true;
+            }
+            else if (neq)
+            {
+                free(str2);
+                return true;
+            }
+            free(str2);
+        }
+        else if (neq)
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static bool xpath_equal_values(struct xpath_parser_context *ctxt)
+{
+    struct xpath_object *arg1, *arg2, *argtmp;
+    bool ret = false;
+
+    if (!ctxt || !ctxt->context)
+        return false;
+
+    arg2 = xpath_pop_value(ctxt);
+    arg1 = xpath_pop_value(ctxt);
+    if (!arg1 || !arg2)
+    {
+        xpath_object_release(ctxt->context, arg1);
+        xpath_object_release(ctxt->context, arg2);
+        xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+        return false;
+    }
+
+    if (arg1 == arg2)
+    {
+        xpath_free_object(arg1);
+        return true;
+    }
+
+    /*
+     *If either argument is a nodeset, it's a 'special case'
+     */
+    if (arg2->type == _XPATH_NODESET || arg1->type == _XPATH_NODESET)
+    {
+        /*
+         *Hack it to assure arg1 is the nodeset
+         */
+        if (arg1->type != _XPATH_NODESET)
+        {
+            argtmp = arg2;
+            arg2 = arg1;
+            arg1 = argtmp;
+        }
+        switch (arg2->type)
+        {
+            case XPATH_NODESET:
+                ret = xpath_equal_nodesets(ctxt, arg1, arg2, 0);
+                break;
+            case XPATH_BOOLEAN:
+                ret = arg1->nodesetval && arg1->nodesetval->count;
+                ret = (ret == arg2->boolval);
+                break;
+            case XPATH_NUMBER:
+                ret = xpath_equal_nodeset_float(ctxt, arg1, arg2->floatval, false);
+                break;
+            case XPATH_STRING:
+                ret = xpath_equal_nodeset_string(arg1, arg2->stringval, false);
+                break;
+            default:
+                ;
+        }
+
+        xpath_object_release(ctxt->context, arg1);
+        xpath_object_release(ctxt->context, arg2);
+        return ret;
+    }
+
+    return xpath_equal_values_common(ctxt, arg1, arg2);
+}
+
+static bool xpath_not_equal_values(struct xpath_parser_context *ctxt)
+{
+    struct xpath_object *arg1, *arg2, *argtmp;
+    bool ret = false;
+
+    if (!ctxt || !ctxt->context)
+        return false;
+
+    arg2 = xpath_pop_value(ctxt);
+    arg1 = xpath_pop_value(ctxt);
+    if (!arg1 || !arg2)
+    {
+        xpath_object_release(ctxt->context, arg1);
+        xpath_object_release(ctxt->context, arg2);
+        xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+        return false;
+    }
+
+    if (arg1 == arg2)
+    {
+        xpath_free_object(arg1);
+        return false;
+    }
+
+    /*
+     *If either argument is a nodeset, it's a 'special case'
+     */
+    if (arg2->type == _XPATH_NODESET || arg1->type == _XPATH_NODESET)
+    {
+        /*
+         *Hack it to assure arg1 is the nodeset
+         */
+        if (arg1->type != _XPATH_NODESET)
+        {
+            argtmp = arg2;
+            arg2 = arg1;
+            arg1 = argtmp;
+        }
+
+        switch (arg2->type)
+        {
+            case XPATH_NODESET:
+                ret = xpath_equal_nodesets(ctxt, arg1, arg2, 1);
+                break;
+            case XPATH_BOOLEAN:
+                ret = arg1->nodesetval && arg1->nodesetval->count;
+                ret = (ret != arg2->boolval);
+                break;
+            case XPATH_NUMBER:
+                ret = xpath_equal_nodeset_float(ctxt, arg1, arg2->floatval, true);
+                break;
+            case XPATH_STRING:
+                ret = xpath_equal_nodeset_string(arg1, arg2->stringval, true);
+                break;
+            default:
+                ;
+        }
+
+        xpath_object_release(ctxt->context, arg1);
+        xpath_object_release(ctxt->context, arg2);
+        return ret;
+    }
+
+    return !xpath_equal_values_common(ctxt, arg1, arg2);
+}
+
+static void xpath_root(struct xpath_parser_context *ctxt)
+{
+    if (!ctxt || !ctxt->context)
+        return;
+
+    xpath_push_value(ctxt, xpath_new_nodeset(ctxt, ctxt->context->root));
+}
+
+static struct domnode * xpath_next_following(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    struct domnode *next;
+
+    if (!ctxt || !ctxt->context)
+        return NULL;
+
+    if (cur && (cur->type != NODE_ATTRIBUTE) && !list_empty(&cur->children))
+        return domnode_get_first_child(cur);
+
+    if (!cur)
+    {
+        cur = ctxt->context->node;
+        if (cur->type == NODE_ATTRIBUTE)
+            cur = xpath_get_parent(cur);
+    }
+    if (!cur) return NULL;
+
+    if ((next = domnode_get_next_sibling(cur))) return next;
+
+    do
+    {
+        cur = xpath_get_parent(cur);
+        if (cur == NULL) break;
+        if (cur == ctxt->context->root) return NULL;
+        if ((next = domnode_get_next_sibling(cur))) return next;
+    } while (cur);
+
+    return cur;
+}
+
+static struct domnode * xpath_next_following_sibling(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    if (!ctxt || !ctxt->context)
+        return NULL;
+
+    if (ctxt->context->node->type == NODE_ATTRIBUTE)
+        return NULL;
+
+    if (cur == ctxt->context->root)
+        return NULL;
+
+    if (!cur)
+        return domnode_get_next_sibling(ctxt->context->node);
+    return domnode_get_next_sibling(cur);
+}
+
+static struct domnode * xpath_next_preceding(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    struct domnode *prev;
+
+    if (!ctxt || !ctxt->context)
+        return NULL;
+
+    if (!cur)
+    {
+        cur = ctxt->context->node;
+        if (!cur)
+            return NULL;
+        if (cur->type == NODE_ATTRIBUTE)
+            cur = xpath_get_parent(cur);
+        ctxt->ancestor = xpath_get_parent(cur);
+    }
+
+    prev = domnode_get_previous_sibling(cur);
+    if (prev && prev->type == NODE_DOCUMENT_TYPE)
+        cur = prev;
+
+    while (domnode_get_previous_sibling(cur) == NULL)
+    {
+        if (!(cur = xpath_get_parent(cur)))
+            return NULL;
+        if (cur == domnode_get_first_child(ctxt->context->root))
+            return NULL;
+        if (cur != ctxt->ancestor)
+            return cur;
+        ctxt->ancestor = xpath_get_parent(cur);
+    }
+    cur = domnode_get_previous_sibling(cur);
+    while (domnode_get_last_child(cur))
+        cur = domnode_get_last_child(cur);
+
+    return cur;
+}
+
+static struct domnode * xpath_next_preceding_sibling(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    struct domnode *prev;
+
+    if (!ctxt || !ctxt->context)
+        return NULL;
+
+    if (ctxt->context->node->type == NODE_ATTRIBUTE)
+        return NULL;
+
+    if (cur == ctxt->context->root)
+        return NULL;
+
+    if (!cur)
+        return domnode_get_previous_sibling(ctxt->context->node);
+
+    prev = domnode_get_previous_sibling(cur);
+    if (prev && prev->type == NODE_DOCUMENT_TYPE)
+    {
+        cur = domnode_get_previous_sibling(cur);
+        if (!cur)
+            return domnode_get_previous_sibling(ctxt->context->node);
+    }
+
+    return domnode_get_previous_sibling(cur);
+}
+
+static struct domnode * xpath_next_descendant(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    if (!ctxt || !ctxt->context)
+        return NULL;
+
+    if (!cur)
+    {
+        if (!ctxt->context->node)
+            return NULL;
+        if (ctxt->context->node->type == NODE_ATTRIBUTE)
+            return NULL;
+
+        if (ctxt->context->node == ctxt->context->root)
+            return domnode_get_first_child(ctxt->context->root);
+
+        return domnode_get_first_child(ctxt->context->node);
+    }
+
+    if (!list_empty(&cur->children))
+    {
+        /*
+         * Do not descend on entities declarations
+         */
+        if (domnode_get_first_child(cur)->type != NODE_ENTITY)
+        {
+            cur = domnode_get_first_child(cur);
+            if (cur->type != NODE_DOCUMENT_TYPE)
+                return cur;
+        }
+    }
+
+    if (cur == ctxt->context->node)
+        return NULL;
+
+    while (domnode_get_next_sibling(cur))
+    {
+        cur = domnode_get_next_sibling(cur);
+        if (cur->type != NODE_ENTITY && cur->type != NODE_DOCUMENT_TYPE)
+            return cur;
+    }
+
+    do
+    {
+        cur = xpath_get_parent(cur);
+        if (cur == NULL) break;
+        if (cur == ctxt->context->node) return NULL;
+        if (domnode_get_next_sibling(cur))
+            return domnode_get_next_sibling(cur);
+    } while (cur);
+
+    return cur;
+}
+
+static struct domnode * xpath_next_descendant_or_self(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    if (!ctxt || !ctxt->context)
+        return NULL;
+
+    if (!cur)
+        return ctxt->context->node;
+
+    if (!ctxt->context->node)
+        return NULL;
+
+    if (ctxt->context->node->type == NODE_ATTRIBUTE)
+        return NULL;
+
+    return xpath_next_descendant(ctxt, cur);
+}
+
+static struct domnode * xpath_next_parent(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    if (!ctxt || !ctxt->context)
+        return NULL;
+
+    if (!cur)
+    {
+        if (ctxt->context->node == NULL) return NULL;
+        return xpath_get_parent(ctxt->context->node);
+    }
+
+    return NULL;
+}
+
+static struct domnode * xpath_next_self(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    if (!ctxt || !ctxt->context)
+        return NULL;
+    if (!cur)
+        return ctxt->context->node;
+    return NULL;
+}
+
+static struct domnode * xpath_next_ancestor(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    if (!ctxt || !ctxt->context) return NULL;
+
+    if (!cur)
+    {
+        if (!ctxt->context->node) return NULL;
+        return xpath_get_parent(ctxt->context->node);
+    }
+    if (cur == domnode_get_first_child(ctxt->context->root))
+        return ctxt->context->root;
+    if (cur == ctxt->context->root)
+        return NULL;
+    return xpath_get_parent(cur);
+}
+
+static struct domnode * xpath_next_ancestor_or_self(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    if (!ctxt || !ctxt->context) return NULL;
+
+    if (!cur)
+        return ctxt->context->node;
+    return xpath_next_ancestor(ctxt, cur);
+}
+
+/* According to XPath model namespace declaration attributes are not attributes,
+   so we need to filter them during traversal. */
+static struct domnode * xpath_get_first_attribute(struct domnode *node)
+{
+    node = domnode_get_first_attribute(node);
+
+    while (node && domnode_is_namespace_declaration(node))
+        node = domnode_get_next_attribute_sibling(node);
+
+    return node;
+}
+
+static struct domnode * xpath_get_next_attribute(struct domnode *node)
+{
+    do
+    {
+        node = domnode_get_next_attribute_sibling(node);
+    }
+    while (node && domnode_is_namespace_declaration(node));
+
+    return node;
+}
+
+static struct domnode * xpath_next_attribute(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    if (!ctxt || !ctxt->context) return NULL;
+
+    if (!ctxt->context->node)
+        return NULL;
+    if (ctxt->context->node->type != NODE_ELEMENT)
+        return NULL;
+
+    if (!cur)
+        return xpath_get_first_attribute(ctxt->context->node);
+
+    return xpath_get_next_attribute(cur);
+}
+
+static struct domnode * xpath_next_child_element(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    struct domnode *next;
+
+    if (!ctxt || !ctxt->context) return NULL;
+
+    if (!cur)
+    {
+        cur = ctxt->context->node;
+        if (!cur) return NULL;
+        /*
+         * Get the first element child.
+         */
+        switch (cur->type)
+        {
+            case NODE_ELEMENT:
+            case NODE_DOCUMENT_FRAGMENT:
+            case NODE_ENTITY_REFERENCE:
+            case NODE_ENTITY:
+                cur = domnode_get_first_child(cur);
+                if (cur)
+                {
+                    if (cur->type == NODE_ELEMENT)
+                        return cur;
+                    do
+                    {
+                        cur = domnode_get_next_sibling(cur);
+                    } while (cur && (cur->type != NODE_ELEMENT));
+                    return cur;
+                }
+                return NULL;
+            case NODE_DOCUMENT:
+                return domnode_get_root_element(cur);
+            default:
+                return NULL;
+        }
+        return NULL;
+    }
+
+    /*
+    * Get the next sibling element node.
+    */
+    next = domnode_get_next_sibling(cur);
+    if (next)
+    {
+        if (next->type == NODE_ELEMENT)
+            return next;
+        cur = next;
+        do
+        {
+            cur = domnode_get_next_sibling(cur);
+        } while (cur && (cur->type != NODE_ELEMENT));
+
+        return cur;
+    }
+
+    return NULL;
+}
+
+static struct domnode * xpath_next_child(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    if (!ctxt || !ctxt->context) return NULL;
+
+    if (!cur)
+    {
+        if (!ctxt->context->node) return NULL;
+        return domnode_get_first_child(ctxt->context->node);
+    }
+
+    if (cur->type == NODE_DOCUMENT)
+        return NULL;
+
+    return domnode_get_next_sibling(cur);
+}
+
+static int xpath_is_positional_predicate(struct xpath_parser_context *ctxt,
+        struct xpath_step_op *op, int *maxPos)
+{
+    struct xpath_step_op *exprOp;
+
+    /*
+    * BIG NOTE: This is not intended for XPATH_OP_FILTER yet!
+    */
+
+    /*
+    * If not -1, then ch1 will point to:
+    * 1) For predicates (XPATH_OP_PREDICATE):
+    *    - an inner predicate operator
+    * 2) For filters (XPATH_OP_FILTER):
+    *    - an inner filter operator OR
+    *    - an expression selecting the node set.
+    *      E.g. "key('a', 'b')" or "(//foo | //bar)".
+    */
+    if ((op->op != XPATH_OP_PREDICATE) && (op->op != XPATH_OP_FILTER))
+        return 0;
+
+    if (op->ch2 != -1)
+        exprOp = &ctxt->comp->steps.values[op->ch2];
+    else
+        return 0;
+
+    if (exprOp &&
+       (exprOp->op == XPATH_OP_VALUE) &&
+        exprOp->value4 &&
+       (((struct xpath_object *)exprOp->value4)->type == _XPATH_NUMBER))
+    {
+        double floatval = ((struct xpath_object *)exprOp->value4)->floatval;
+
+        if ((floatval > INT_MIN) && (floatval < INT_MAX))
+        {
+            *maxPos = (int) floatval;
+            if (floatval == (double) *maxPos)
+                return 1;
+        }
+    }
+
+    return 0;
+}
+
+static bool xpath_has_namespace(struct xpath_parser_context *ctxt, struct domnode *node)
+{
+    for (size_t i = 0; i < ctxt->namespaces.count; ++i)
+    {
+        const struct domnode *n = ctxt->namespaces.nodes[i];
+
+        if (!wcscmp(node->name, n->name))
+            return true;
+    }
+
+    return false;
+}
+
+static void xpath_eval_add_namespace(struct xpath_parser_context *ctxt, struct domnode *node)
+{
+    if (xpath_array_reserve(ctxt, (void **)&ctxt->namespaces.nodes, &ctxt->namespaces.capacity,
+            ctxt->namespaces.count + 1, sizeof(*ctxt->namespaces.nodes)))
+    {
+        ctxt->namespaces.nodes[ctxt->namespaces.count++] = node;
+    }
+}
+
+static void xpath_eval_collect_namespaces(struct xpath_parser_context *ctxt)
+{
+    struct domnode *node, *attr;
+
+    free(ctxt->namespaces.nodes);
+    memset(&ctxt->namespaces, 0, sizeof(ctxt->namespaces));
+
+    node = ctxt->context->node;
+    while (node)
+    {
+        if (node->type == NODE_ELEMENT)
+        {
+            attr = domnode_get_first_attribute(node);
+
+            while (attr)
+            {
+                if (domnode_is_namespace_declaration(attr))
+                {
+                    if (!xpath_has_namespace(ctxt, attr))
+                        xpath_eval_add_namespace(ctxt, attr);
+                }
+
+                attr = domnode_get_next_attribute_sibling(attr);
+            }
+        }
+
+        node = xpath_get_parent(node);
+    }
+
+    /* Implicit xmlns:xml namespace, carried by document node for a lack of better place. */
+    attr = domnode_get_first_attribute(ctxt->context->node->owner);
+    xpath_eval_add_namespace(ctxt, attr);
+}
+
+static struct domnode * xpath_next_namespace(struct xpath_parser_context *ctxt, struct domnode *cur)
+{
+    if (!ctxt || !ctxt->context) return NULL;
+
+    if (ctxt->context->node->type != NODE_ELEMENT)
+        return NULL;
+
+    if (!cur)
+        xpath_eval_collect_namespaces(ctxt);
+
+    if (ctxt->namespaces.count > 0)
+    {
+        return ctxt->namespaces.nodes[--ctxt->namespaces.count];
+    }
+    else
+    {
+        free(ctxt->namespaces.nodes);
+        memset(&ctxt->namespaces, 0, sizeof(ctxt->namespaces));
+    }
+
+    return NULL;
+}
+
+static void xpath_nodeset_filter(struct xpath_parser_context *ctxt, struct xpath_nodeset *set,
+        int filterOpIndex, int minPos, int maxPos, bool hasNsNodes);
+
+static void xpath_comp_op_eval_predicate(struct xpath_parser_context *ctxt, struct xpath_step_op *op,
+        struct xpath_nodeset *set, int minPos, int maxPos, bool hasNsNodes)
+{
+    if (op->ch1 != -1)
+    {
+        struct xpath_comp_expr *comp = ctxt->comp;
+
+        /* Process inner predicates first. */
+        if (comp->steps.values[op->ch1].op != XPATH_OP_PREDICATE)
+            return xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+
+        if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
+            return xpath_parser_context_set_error(ctxt, XPATH_RECURSION_LIMIT_EXCEEDED);
+
+        ctxt->context->depth += 1;
+        xpath_comp_op_eval_predicate(ctxt, &comp->steps.values[op->ch1], set, 1, set->count, hasNsNodes);
+        ctxt->context->depth -= 1;
+        if (ctxt->error != XPATH_EXPRESSION_OK) return;
+    }
+
+    if (op->ch2 != -1)
+        xpath_nodeset_filter(ctxt, set, op->ch2, minPos, maxPos, hasNsNodes);
+}
+
+static const WCHAR * xpath_ns_lookup(struct xpath_context *ctxt, const WCHAR *prefix)
+{
+    struct xpath_namespace *ns;
+
+    if (!ctxt || !prefix)
+        return NULL;
+
+    if (!wcscmp(prefix, L"xml"))
+        return L"http://www.w3.org/XML/1998/namespace";
+
+    LIST_FOR_EACH_ENTRY(ns, &ctxt->properties->namespaces.entries, struct xpath_namespace, entry)
+    {
+        if (!wcscmp(ns->prefix, prefix))
+            return ns->uri;
+    }
+
+    return NULL;
+}
+
+static struct xpath_nodeset * xpath_nodeset_merge_and_clear(struct xpath_parser_context *ctxt,
+        struct xpath_nodeset *set1, struct xpath_nodeset *set2)
+{
+    struct domnode *n1, *n2;
+    int i, j, initNbSet1;
+
+    initNbSet1 = set1->count;
+    for (i = 0; i < set2->count; ++i)
+    {
+        n2 = set2->nodes[i];
+        /* Skip duplicates. */
+        for (j = 0; j < initNbSet1; ++j)
+        {
+            n1 = set1->nodes[j];
+            if (n1 == n2)
+            {
+                goto skip_node;
+            }
+        }
+
+        xpath_nodeset_add_unique(ctxt, set1, n2);
+skip_node:
+        set2->nodes[i] = NULL;
+    }
+
+    set2->count = 0;
+    return set1;
+}
+
+static struct xpath_nodeset * xpath_nodeset_merge_and_clear_nodup(struct xpath_parser_context *ctxt,
+        struct xpath_nodeset *set1, struct xpath_nodeset *set2)
+{
+    struct domnode *n2;
+    int i;
+
+    for (i = 0; i < set2->count; ++i)
+    {
+        n2 = set2->nodes[i];
+        xpath_nodeset_add_unique(ctxt, set1, n2);
+        set2->nodes[i] = NULL;
+    }
+
+    set2->count = 0;
+    return set1;
+}
+
+static const WCHAR * xpath_get_default_ns_uri(const struct domnode *node)
+{
+    struct domnode *attr;
+
+    LIST_FOR_EACH_ENTRY(attr, &node->attributes, struct domnode, entry)
+    {
+        if (!wcscmp(attr->qname, L"xmlns"))
+        {
+            node = domnode_get_first_child(attr);
+            return node ? node->data : NULL;
+        }
+    }
+
+    return NULL;
+}
+
+static const WCHAR * xpath_get_node_uri(const struct domnode *node)
+{
+    const WCHAR *uri;
+
+    if (node->uri)
+        return node->uri;
+
+    /* For attributes go for the containing element. */
+    if (node->type == NODE_ATTRIBUTE)
+    {
+        node = xpath_get_parent(node);
+        if (!node) return NULL;
+    }
+
+    /* Otherwise traverse looking for default namespace. */
+    do
+    {
+        uri = xpath_get_default_ns_uri(node);
+        node = xpath_get_parent(node);
+    } while (node);
+
+    return uri;
+}
+
+static int xpath_nodeset_add_namespace(struct xpath_parser_context *ctxt,
+        struct xpath_nodeset *set, struct domnode *node, struct domnode *ns)
+{
+    /* Check for duplicates */
+    for (size_t i = 0; i < set->count; ++i)
+    {
+        if (domnode_is_namespace_declaration(set->nodes[i])
+                && (xpath_get_parent(set->nodes[i]) == node)
+                && !wcscmp(set->nodes[i]->name, ns->name))
+        {
+            return 0;
+        }
+    }
+
+    return xpath_nodeset_add_unique(ctxt, set, ns);
+}
+
+static int xpath_node_collect_and_test(struct xpath_parser_context *ctxt,
+        struct xpath_step_op *op, struct domnode **first, struct domnode **last, bool toBool)
+{
+#define XP_TEST_HIT \
+    if (hasAxisRange) { \
+        if (++pos == maxPos) { \
+            xpath_nodeset_add_unique(ctxt, seq, cur); \
+            goto axis_range_end; } \
+    } else { \
+        xpath_nodeset_add_unique(ctxt, seq, cur); \
+        if (breakOnFirstHit) goto first_hit; }
+
+#define XP_TEST_HIT_NS \
+    if (hasAxisRange) { \
+        if (++pos == maxPos) { \
+            hasNsNodes = true; \
+            xpath_nodeset_add_namespace(ctxt, seq, xpctxt->node, cur); \
+        goto axis_range_end; } \
+    } else { \
+        hasNsNodes = true; \
+        xpath_nodeset_add_namespace(ctxt, seq, xpctxt->node, cur); \
+        if (breakOnFirstHit) goto first_hit; }
+
+    enum xpath_axis axis = op->value;
+    enum xpath_test test = op->value2;
+    enum xpath_type type = op->value3;
+    const WCHAR *prefix = op->value4;
+    const WCHAR *name = op->value5;
+    const WCHAR *uri = NULL, *node_uri;
+
+    int total = 0;
+    bool hasNsNodes = false;
+    /* The popped object holding the context nodes */
+    struct xpath_object *obj;
+    /* The set of context nodes for the node tests */
+    struct xpath_nodeset *contextSeq;
+    int contextIdx;
+    struct domnode *contextNode;
+    /* The final resulting node set wrt to all context nodes */
+    struct xpath_nodeset *outSeq;
+    /*
+    * The temporary resulting node set wrt 1 context node.
+    * Used to feed predicate evaluation.
+    */
+    struct xpath_nodeset *seq;
+    struct domnode *cur;
+    /* First predicate operator */
+    struct xpath_step_op *predOp;
+    int maxPos; /* The requested position() (when a "[n]" predicate) */
+    int hasPredicateRange, pos;
+    bool breakOnFirstHit, hasAxisRange;
+
+    xpath_traversal_function next = NULL;
+    xpath_nodeset_merge_function mergeAndClear;
+    struct domnode *oldContextNode;
+    struct xpath_context *xpctxt = ctxt->context;
+
+    if (!xpath_check_type(ctxt, _XPATH_NODESET))
+        return 0;
+    obj = xpath_pop_value(ctxt);
+
+    if (prefix)
+    {
+        uri  = xpath_ns_lookup(xpctxt, prefix);
+        if (!uri)
+        {
+            xpath_object_release(xpctxt, obj);
+            xpath_parser_context_set_error(ctxt, XPATH_UNDEF_PREFIX_ERROR);
+            return 0;
+        }
+    }
+
+    mergeAndClear = xpath_nodeset_merge_and_clear;
+    switch (axis)
+    {
+        case AXIS_ANCESTOR:
+            first = NULL;
+            next = xpath_next_ancestor;
+            break;
+        case AXIS_ANCESTOR_OR_SELF:
+            first = NULL;
+            next = xpath_next_ancestor_or_self;
+            break;
+        case AXIS_ATTRIBUTE:
+            first = NULL;
+            last = NULL;
+            next = xpath_next_attribute;
+            mergeAndClear = xpath_nodeset_merge_and_clear_nodup;
+            break;
+        case AXIS_CHILD:
+            last = NULL;
+            if (((test == NODE_TEST_NAME) || (test == NODE_TEST_ALL)) && (type == NODE_TYPE_NODE))
+            {
+                /*
+                 * Optimization if an element node type is 'element'.
+                 */
+                next = xpath_next_child_element;
+            }
+            else
+            {
+                next = xpath_next_child;
+            }
+            mergeAndClear = xpath_nodeset_merge_and_clear_nodup;
+            break;
+        case AXIS_DESCENDANT:
+            last = NULL;
+            next = xpath_next_descendant;
+            break;
+        case AXIS_DESCENDANT_OR_SELF:
+            last = NULL;
+            next = xpath_next_descendant_or_self;
+            break;
+        case AXIS_FOLLOWING:
+            last = NULL;
+            next = xpath_next_following;
+            break;
+        case AXIS_FOLLOWING_SIBLING:
+            last = NULL;
+            next = xpath_next_following_sibling;
+            break;
+        case AXIS_NAMESPACE:
+            first = NULL;
+            last = NULL;
+            next = xpath_next_namespace;
+            mergeAndClear = xpath_nodeset_merge_and_clear_nodup;
+            break;
+        case AXIS_PARENT:
+            first = NULL;
+            next = xpath_next_parent;
+            break;
+        case AXIS_PRECEDING:
+            first = NULL;
+            next = xpath_next_preceding;
+            break;
+        case AXIS_PRECEDING_SIBLING:
+            first = NULL;
+            next = xpath_next_preceding_sibling;
+            break;
+        case AXIS_SELF:
+            first = NULL;
+            last = NULL;
+            next = xpath_next_self;
+            mergeAndClear = xpath_nodeset_merge_and_clear_nodup;
+            break;
+        default:
+            ;
+    }
+
+    if (!next)
+    {
+        xpath_object_release(xpctxt, obj);
+        return 0;
+    }
+
+    contextSeq = obj->nodesetval;
+    if (!contextSeq || contextSeq->count <= 0)
+    {
+        xpath_object_release(xpctxt, obj);
+        xpath_push_value(ctxt, xpath_wrap_nodeset(ctxt, NULL));
+        return 0;
+    }
+
+    /*
+    * Predicate optimization ---------------------------------------------
+    * If this step has a last predicate, which contains a position(),
+    * then we'll optimize (although not exactly "position()", but only
+    * the  short-hand form, i.e., "[n]".
+    *
+    * Example - expression "/foo[parent::bar][1]":
+    *
+    * COLLECT 'child' 'name' 'node' foo    -- op (we are here)
+    *   ROOT                               -- op->ch1
+    *   PREDICATE                          -- op->ch2 (predOp)
+    *     PREDICATE                          -- predOp->ch1 = [parent::bar]
+    *       SORT
+    *         COLLECT  'parent' 'name' 'node' bar
+    *           NODE
+    *     ELEM Object is a number : 1        -- predOp->ch2 = [1]
+    *
+    */
+    maxPos = 0;
+    predOp = NULL;
+    hasPredicateRange = 0;
+    hasAxisRange = false;
+    if (op->ch2 != -1)
+    {
+        /*
+        * There's at least one predicate. 16 == XPATH_OP_PREDICATE
+        */
+        predOp = &ctxt->comp->steps.values[op->ch2];
+        if (xpath_is_positional_predicate(ctxt, predOp, &maxPos))
+        {
+            if (predOp->ch1 != -1)
+            {
+                predOp = &ctxt->comp->steps.values[predOp->ch1];
+                hasPredicateRange = 1;
+            }
+            else
+            {
+                predOp = NULL;
+                hasAxisRange = true;
+            }
+        }
+    }
+    breakOnFirstHit = ((toBool) && (predOp == NULL)) ? 1 : 0;
+
+    /*
+    * Axis traversal -----------------------------------------------------
+    */
+    /*
+     * 2.3 Node Tests
+     *  - For the attribute axis, the principal node type is attribute.
+     *  - For the namespace axis, the principal node type is namespace.
+     *  - For other axes, the principal node type is element.
+     *
+     * A node test * is true for any node of the
+     * principal node type. For example, child::* will
+     * select all element children of the context node
+     */
+    oldContextNode = xpctxt->node;
+    outSeq = NULL;
+    seq = NULL;
+    contextNode = NULL;
+    contextIdx = 0;
+
+    while (((contextIdx < contextSeq->count) || contextNode) && (ctxt->error == XPATH_EXPRESSION_OK))
+    {
+        xpctxt->node = contextSeq->nodes[contextIdx++];
+
+        if (!seq)
+        {
+            if (!(seq = xpath_create_nodeset(ctxt, NULL)))
+            {
+                total = 0;
+                goto error;
+            }
+        }
+
+        /*
+        * Traverse the axis and test the nodes.
+        */
+        pos = 0;
+        cur = NULL;
+        hasNsNodes = false;
+
+        do
+        {
+            if (ctxt->context->opLimit && xpath_check_op_limit(ctxt, 1) < 0)
+                goto error;
+
+            if (!(cur = next(ctxt, cur)))
+                break;
+
+            if (first && *first)
+            {
+                if (*first == cur)
+                    break;
+                if (((total % 256) == 0) && (xpath_cmp_nodes(*first, cur) >= 0))
+                    break;
+            }
+
+            if (last && *last)
+            {
+                if (*last == cur)
+                    break;
+                if (((total % 256) == 0) && (xpath_cmp_nodes(cur, *last) >= 0))
+                    break;
+            }
+
+            total++;
+
+            node_uri = xpath_get_node_uri(cur);
+            switch (test)
+            {
+                case NODE_TEST_NONE:
+                    total = 0;
+                    goto error;
+                case NODE_TEST_TYPE:
+                    if (type == NODE_TYPE_NODE)
+                    {
+                        switch (cur->type)
+                        {
+                            case NODE_DOCUMENT:
+                            case NODE_ELEMENT:
+                            case NODE_PROCESSING_INSTRUCTION:
+                            case NODE_COMMENT:
+                            case NODE_CDATA_SECTION:
+                            case NODE_TEXT:
+                                XP_TEST_HIT
+                                break;
+
+                            case NODE_ATTRIBUTE:
+                                if (domnode_is_namespace_declaration(cur))
+                                {
+                                    if (axis == AXIS_NAMESPACE)
+                                    {
+                                        XP_TEST_HIT_NS
+                                    }
+                                    else
+                                    {
+                                        hasNsNodes = true;
+                                        XP_TEST_HIT
+                                    }
+                                }
+                                else
+                                {
+                                    XP_TEST_HIT
+                                }
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                    else if (cur->type == (DOMNodeType)type)
+                    {
+                        if (domnode_is_namespace_declaration(cur))
+                            XP_TEST_HIT_NS
+                        else
+                            XP_TEST_HIT
+                    }
+                    else if (type == NODE_TYPE_TEXT && cur->type == NODE_CDATA_SECTION)
+                    {
+                        XP_TEST_HIT
+                    }
+                    break;
+
+                case NODE_TEST_PI:
+                    if (cur->type == NODE_PROCESSING_INSTRUCTION && (!name || !wcscmp(name, cur->name)))
+                    {
+                        XP_TEST_HIT
+                    }
+                    break;
+                case NODE_TEST_ALL:
+                    if (axis == AXIS_ATTRIBUTE)
+                    {
+                        if (cur->type == NODE_ATTRIBUTE)
+                        {
+                            if (!uri || (node_uri && !wcscmp(node_uri, uri)))
+                            {
+                                XP_TEST_HIT
+                            }
+                        }
+                    }
+                    else if (axis == AXIS_NAMESPACE)
+                    {
+                        if (domnode_is_namespace_declaration(cur))
+                        {
+                            XP_TEST_HIT_NS
+                        }
+                    }
+                    else
+                    {
+                        if (cur->type == NODE_ELEMENT)
+                        {
+                            if (!uri || (node_uri && !wcscmp(node_uri, uri)))
+                            {
+                                XP_TEST_HIT
+                            }
+                        }
+                    }
+                    break;
+                case NODE_TEST_NAME:
+                    if (axis == AXIS_ATTRIBUTE)
+                    {
+                        if (cur->type != NODE_ATTRIBUTE)
+                            break;
+                    }
+                    else if (axis == AXIS_NAMESPACE)
+                    {
+                        if (!domnode_is_namespace_declaration(cur))
+                            break;
+                    }
+                    else
+                    {
+                        if (cur->type != NODE_ELEMENT)
+                            break;
+                    }
+
+                    switch (cur->type)
+                    {
+                        case NODE_ELEMENT:
+                            if (!wcscmp(name, cur->name))
+                            {
+                                node_uri = xpath_get_node_uri(cur);
+
+                                if (prefix)
+                                {
+                                    if (node_uri && !wcscmp(uri, node_uri))
+                                    {
+                                        XP_TEST_HIT
+                                    }
+                                }
+                                else if (!node_uri)
+                                {
+                                    XP_TEST_HIT
+                                }
+                            }
+                            break;
+                        case NODE_ATTRIBUTE:
+                            if (domnode_is_namespace_declaration(cur))
+                            {
+                                if (cur->name && name && !wcscmp(cur->name, name))
+                                {
+                                    XP_TEST_HIT_NS
+                                }
+                            }
+                            else if (!wcscmp(name, cur->name))
+                            {
+                                node_uri = xpath_get_node_uri(cur);
+
+                                if (prefix)
+                                {
+                                    if (node_uri && !wcscmp(uri, node_uri))
+                                    {
+                                        XP_TEST_HIT
+                                    }
+                                }
+                                else if (!node_uri)
+                                {
+                                    XP_TEST_HIT
+                                }
+                            }
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
+            } /* switch(test) */
+        } while (cur && (ctxt->error == XPATH_EXPRESSION_OK));
+
+        goto apply_predicates;
+
+axis_range_end: /* ----------------------------------------------------- */
+        /*
+        * We have a "/foo[n]", and position() = n was reached.
+        * Note that we can have as well "/foo/::parent::foo[1]", so
+        * a duplicate-aware merge is still needed.
+        * Merge with the result.
+        */
+        if (!outSeq)
+        {
+            outSeq = seq;
+            seq = NULL;
+        }
+        else
+        {
+            outSeq = mergeAndClear(ctxt, outSeq, seq);
+        }
+        continue;
+
+first_hit: /* ---------------------------------------------------------- */
+        /*
+        * Break if only a true/false result was requested and
+        * no predicates existed and a node test succeeded.
+        */
+        if (!outSeq)
+        {
+            outSeq = seq;
+            seq = NULL;
+        }
+        else
+        {
+            outSeq = mergeAndClear(ctxt, outSeq, seq);
+        }
+        break;
+
+apply_predicates: /* --------------------------------------------------- */
+        if (ctxt->error != XPATH_EXPRESSION_OK)
+            goto error;
+
+        /* Apply predicates. */
+        if (predOp && (seq->count > 0))
+        {
+            /*
+            * E.g. when we have a "/foo[some expression][n]".
+            */
+
+            /*
+            * Iterate over all predicates, starting with the outermost
+            * predicate.
+            */
+            if (hasPredicateRange != 0)
+                xpath_comp_op_eval_predicate(ctxt, predOp, seq, maxPos, maxPos, hasNsNodes);
+            else
+                xpath_comp_op_eval_predicate(ctxt, predOp, seq, 1, seq->count, hasNsNodes);
+
+            if (ctxt->error != XPATH_EXPRESSION_OK)
+            {
+                total = 0;
+                goto error;
+            }
+        }
+
+        if (seq->count > 0)
+        {
+            /* Add to result set. */
+            if (!outSeq )
+            {
+                outSeq = seq;
+                seq = NULL;
+            }
+            else
+            {
+                outSeq = mergeAndClear(ctxt, outSeq, seq);
+            }
+        }
+    }
+
+error:
+    xpath_object_release(xpctxt, obj);
+
+    /*
+    * Ensure we return at least an empty set.
+    */
+    if (!outSeq)
+    {
+        if (seq && seq->count == 0)
+            outSeq = seq;
+        else
+           outSeq = xpath_create_nodeset(ctxt, NULL);
+    }
+
+    if (seq && (seq != outSeq))
+        xpath_free_nodeset(seq);
+
+    xpath_push_value(ctxt, xpath_wrap_nodeset(ctxt, outSeq));
+    xpctxt->node = oldContextNode;
+
+    free(ctxt->namespaces.nodes);
+
+    return total;
+}
+
+static double xpath_cast_node_to_number(struct domnode *node);
+
+static int xpath_compare_nodesets(struct xpath_parser_context *ctxt, bool inf, bool strict,
+        struct xpath_object *arg1, struct xpath_object *arg2)
+{
+    struct xpath_nodeset *ns1, *ns2;
+    double *values2, val1;
+    bool init = false;
+    bool ret = false;
+    int i, j;
+
+    if (!arg1 || (arg1->type != _XPATH_NODESET))
+    {
+        xpath_free_object(arg2);
+        return false;
+    }
+
+    if (!arg2 || (arg2->type != _XPATH_NODESET))
+    {
+        xpath_free_object(arg1);
+        xpath_free_object(arg2);
+        return false;
+    }
+
+    ns1 = arg1->nodesetval;
+    ns2 = arg2->nodesetval;
+
+    if (!ns1 || ns1->count <= 0)
+    {
+        xpath_free_object(arg1);
+        xpath_free_object(arg2);
+        return false;
+    }
+
+    if (!ns2 || ns2->count <= 0)
+    {
+        xpath_free_object(arg1);
+        xpath_free_object(arg2);
+        return false;
+    }
+
+    values2 = xpath_malloc(ctxt, ns2->count * sizeof(*values2));
+    if (!values2)
+    {
+        xpath_free_object(arg1);
+        xpath_free_object(arg2);
+        return false;
+    }
+
+    for (i = 0; i < ns1->count; ++i)
+    {
+        val1 = xpath_cast_node_to_number(ns1->nodes[i]);
+        if (isnan(val1))
+            continue;
+
+        for (j = 0;j < ns2->count; ++j)
+        {
+            if (!init)
+                values2[j] = xpath_cast_node_to_number(ns2->nodes[j]);
+
+            if (isnan(values2[j]))
+                continue;
+            if (inf && strict)
+                ret = (val1 < values2[j]);
+            else if (inf && !strict)
+                ret = (val1 <= values2[j]);
+            else if (!inf && strict)
+                ret = (val1 > values2[j]);
+            else if (!inf && !strict)
+                ret = (val1 >= values2[j]);
+            if (ret)
+                break;
+        }
+        if (ret)
+            break;
+
+        init = true;
+    }
+
+    free(values2);
+    xpath_free_object(arg1);
+    xpath_free_object(arg2);
+    return ret;
+}
+
+static bool xpath_compare_values(struct xpath_parser_context *ctxt, bool inf, bool strict);
+
+static bool xpath_compare_nodeset_float(struct xpath_parser_context *ctxt, bool inf, bool strict,
+        struct xpath_object *arg, struct xpath_object *f)
+{
+    struct xpath_nodeset *ns;
+    bool ret = false;
+    WCHAR *str2;
+    int i;
+
+    if (!f || !arg || arg->type != _XPATH_NODESET)
+    {
+        xpath_object_release(ctxt->context, arg);
+        xpath_object_release(ctxt->context, f);
+        return false;
+    }
+
+    if ((ns = arg->nodesetval))
+    {
+        for (i = 0; i < ns->count; ++i)
+        {
+            str2 = xpath_cast_node_to_string(ns->nodes[i]);
+            if (str2)
+            {
+                xpath_push_value(ctxt, xpath_new_string(ctxt, str2));
+                free(str2);
+                xpath_builtin_number(ctxt, 1);
+                xpath_push_value(ctxt, xpath_object_copy(ctxt, f));
+                ret = xpath_compare_values(ctxt, inf, strict);
+                if (ret)
+                    break;
+            }
+        }
+    }
+
+    xpath_object_release(ctxt->context, arg);
+    xpath_object_release(ctxt->context, f);
+    return ret;
+}
+
+static bool xpath_compare_nodeset_string(struct xpath_parser_context *ctxt, bool inf, bool strict,
+        struct xpath_object *arg, struct xpath_object *s)
+{
+    struct xpath_nodeset *ns;
+    bool ret = false;
+    WCHAR *str2;
+
+    if (!s || !arg || arg->type != _XPATH_NODESET)
+    {
+        xpath_object_release(ctxt->context, arg);
+        xpath_object_release(ctxt->context, s);
+        return false;
+    }
+
+    if ((ns = arg->nodesetval))
+    {
+        for (int i = 0; i < ns->count; ++i)
+        {
+            str2 = xpath_cast_node_to_string(ns->nodes[i]);
+            if (str2)
+            {
+                xpath_push_value(ctxt, xpath_new_string(ctxt, str2));
+                free(str2);
+                xpath_push_value(ctxt, xpath_object_copy(ctxt, s));
+                ret = xpath_compare_values(ctxt, inf, strict);
+                if (ret)
+                    break;
+            }
+        }
+    }
+
+    xpath_object_release(ctxt->context, arg);
+    xpath_object_release(ctxt->context, s);
+    return ret;
+}
+
+static bool xpath_compare_nodeset_value(struct xpath_parser_context *ctxt, bool inf, bool strict,
+        struct xpath_object *arg, struct xpath_object *val)
+{
+    if (!val || !arg || arg->type != _XPATH_NODESET)
+        return false;
+
+    switch (val->type)
+    {
+        case _XPATH_NUMBER:
+            return xpath_compare_nodeset_float(ctxt, inf, strict, arg, val);
+        case _XPATH_NODESET:
+            return xpath_compare_nodesets(ctxt, inf, strict, arg, val);
+        case _XPATH_STRING:
+            return xpath_compare_nodeset_string(ctxt, inf, strict, arg, val);
+        case _XPATH_BOOLEAN:
+            xpath_push_value(ctxt, arg);
+            xpath_builtin_boolean(ctxt, 1);
+            xpath_push_value(ctxt, val);
+            return xpath_compare_values(ctxt, inf, strict);
+        default:
+            xpath_object_release(ctxt->context, arg);
+            xpath_object_release(ctxt->context, val);
+            xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+    }
+
+    return false;
+}
+
+static bool xpath_compare_values(struct xpath_parser_context *ctxt, bool inf, bool strict)
+{
+    struct xpath_object *arg1, *arg2;
+    int arg1i = 0, arg2i = 0;
+    bool ret = false;
+
+    if (!ctxt || !ctxt->context) return false;
+
+    arg2 = xpath_pop_value(ctxt);
+    arg1 = xpath_pop_value(ctxt);
+    if (!arg1 || !arg2)
+    {
+        xpath_object_release(ctxt->context, arg1);
+        xpath_object_release(ctxt->context, arg2);
+        xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+        return false;
+    }
+
+    if (arg2->type == _XPATH_NODESET || arg1->type == _XPATH_NODESET)
+    {
+        /*
+         * If either argument is a XPATH_NODESET the two arguments
+         * are not freed from within this routine.
+         */
+        if (arg2->type == _XPATH_NODESET && arg1->type == _XPATH_NODESET)
+        {
+            ret = xpath_compare_nodesets(ctxt, inf, strict, arg1, arg2);
+        }
+        else
+        {
+            if (arg1->type == _XPATH_NODESET)
+                ret = xpath_compare_nodeset_value(ctxt, !inf, strict, arg1, arg2);
+            else
+                ret = xpath_compare_nodeset_value(ctxt, !inf, strict, arg2, arg1);
+        }
+
+        return ret;
+    }
+
+    if (arg1->type != _XPATH_NUMBER)
+    {
+        xpath_push_value(ctxt, arg1);
+        xpath_builtin_number(ctxt, 1);
+        arg1 = xpath_pop_value(ctxt);
+    }
+
+    if (arg2->type != _XPATH_NUMBER)
+    {
+        xpath_push_value(ctxt, arg2);
+        xpath_builtin_number(ctxt, 1);
+        arg2 = xpath_pop_value(ctxt);
+    }
+    if (ctxt->error)
+        goto error;
+
+    /* Hand check NaN and Infinity comparisons */
+    if (isnan(arg1->floatval) || isnan(arg2->floatval))
+    {
+        ret = false;
+    }
+    else
+    {
+        arg1i = xpath_isinf(arg1->floatval);
+        arg2i = xpath_isinf(arg2->floatval);
+        if (inf && strict)
+        {
+            if ((arg1i == -1 && arg2i != -1) || (arg2i == 1 && arg1i != 1))
+                ret = true;
+            else if (arg1i == 0 && arg2i == 0)
+                ret = (arg1->floatval < arg2->floatval);
+            else
+                ret = false;
+        }
+        else if (inf && !strict)
+        {
+            if (arg1i == -1 || arg2i == 1)
+                ret = true;
+            else if (arg1i == 0 && arg2i == 0)
+                ret = (arg1->floatval <= arg2->floatval);
+            else
+                ret = false;
+        }
+        else if (!inf && strict)
+        {
+            if ((arg1i == 1 && arg2i != 1) || (arg2i == -1 && arg1i != -1))
+                ret = true;
+            else if (arg1i == 0 && arg2i == 0)
+                ret = (arg1->floatval > arg2->floatval);
+            else
+                ret = false;
+        }
+        else if (!inf && !strict)
+        {
+            if (arg1i == 1 || arg2i == -1)
+                ret = true;
+            else if (arg1i == 0 && arg2i == 0)
+                ret = (arg1->floatval >= arg2->floatval);
+            else
+                ret = false;
+        }
+    }
+
+error:
+    xpath_object_release(ctxt->context, arg1);
+    xpath_object_release(ctxt->context, arg2);
+    return ret;
+}
+
+static int xpath_comp_op_eval(struct xpath_parser_context *ctxt, struct xpath_step_op *op);
+
+static void xpath_comp_swap(struct xpath_step_op *op)
+{
+    int tmp;
+
+    tmp = op->ch1;
+    op->ch1 = op->ch2;
+    op->ch2 = tmp;
+}
+
+static int xpath_comp_op_eval_first(struct xpath_parser_context *ctxt, struct xpath_step_op *op,
+        struct domnode **first)
+{
+    struct xpath_object *arg1, *arg2;
+    struct xpath_comp_expr *comp;
+    int total = 0, cur;
+
+    if (ctxt->error != XPATH_EXPRESSION_OK)
+        return 0;
+    if (ctxt->context->opLimit && xpath_check_op_limit(ctxt, 1) < 0)
+        return 0;
+    if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_RECURSION_LIMIT_EXCEEDED);
+        return 0;
+    }
+    ctxt->context->depth += 1;
+
+    comp = ctxt->comp;
+    switch (op->op)
+    {
+        case XPATH_OP_END:
+            break;
+        case XPATH_OP_UNION:
+            total = xpath_comp_op_eval_first(ctxt, &comp->steps.values[op->ch1], first);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+
+            if (ctxt->value
+                && (ctxt->value->type == _XPATH_NODESET)
+                && ctxt->value->nodesetval
+                && (ctxt->value->nodesetval->count >= 1))
+            {
+                /*
+                 * limit tree traversing to first node in the result
+                 */
+                xpath_nodeset_sort(ctxt->value->nodesetval);
+                *first = ctxt->value->nodesetval->nodes[0];
+            }
+            cur = xpath_comp_op_eval_first(ctxt, &comp->steps.values[op->ch2], first);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+
+            arg2 = xpath_pop_value(ctxt);
+            arg1 = xpath_pop_value(ctxt);
+
+            if (!arg1 || arg1->type != _XPATH_NODESET ||
+                !arg2 || arg2->type != _XPATH_NODESET)
+            {
+                xpath_object_release(ctxt->context, arg1);
+                xpath_object_release(ctxt->context, arg2);
+                xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+                return 0;
+            }
+
+            if (ctxt->context->opLimit &&
+                ((arg1->nodesetval && (xpath_check_op_limit(ctxt, arg1->nodesetval->count) < 0)) ||
+                 (arg2->nodesetval && (xpath_check_op_limit(ctxt, arg2->nodesetval->count) < 0))))
+            {
+                xpath_object_release(ctxt->context, arg1);
+                xpath_object_release(ctxt->context, arg2);
+                break;
+            }
+
+            arg1->nodesetval = xpath_nodeset_merge(ctxt, arg1->nodesetval, arg2->nodesetval);
+            xpath_push_value(ctxt, arg1);
+            xpath_object_release(ctxt->context, arg2);
+            if (total > cur)
+                xpath_comp_swap(op);
+            total += cur;
+            break;
+        case XPATH_OP_ROOT:
+            xpath_root(ctxt);
+            break;
+        case XPATH_OP_NODE:
+            if (op->ch1 != -1)
+                total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            if (op->ch2 != -1)
+                total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch2]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            xpath_push_value(ctxt, xpath_new_nodeset(ctxt, ctxt->context->node));
+            break;
+        case XPATH_OP_COLLECT:
+            if (op->ch1 == -1)
+                break;
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            total += xpath_node_collect_and_test(ctxt, op, first, NULL, false);
+            break;
+        case XPATH_OP_VALUE:
+            xpath_push_value(ctxt, xpath_object_copy(ctxt, (struct xpath_object *)op->value4));
+            break;
+        case XPATH_OP_SORT:
+            if (op->ch1 != -1)
+                total += xpath_comp_op_eval_first(ctxt, &comp->steps.values[op->ch1], first);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+
+            if (ctxt->value && ctxt->value->type == _XPATH_NODESET)
+                xpath_nodeset_sort(ctxt->value->nodesetval);
+            break;
+        default:
+            total += xpath_comp_op_eval(ctxt, op);
+            break;
+    }
+
+    ctxt->context->depth -= 1;
+    return total;
+}
+
+static int xpath_comp_op_eval_last(struct xpath_parser_context *ctxt, struct xpath_step_op *op, struct domnode **last)
+{
+    struct xpath_object *arg1, *arg2;
+    struct xpath_comp_expr *comp;
+    int total = 0, cur;
+
+    if (ctxt->error != XPATH_EXPRESSION_OK)
+        return 0;
+    if (ctxt->context->opLimit && xpath_check_op_limit(ctxt, 1) < 0)
+        return 0;
+    if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_RECURSION_LIMIT_EXCEEDED);
+        return 0;
+    }
+    ctxt->context->depth += 1;
+
+    comp = ctxt->comp;
+    switch (op->op)
+    {
+        case XPATH_OP_END:
+            break;
+        case XPATH_OP_UNION:
+            total = xpath_comp_op_eval_last(ctxt, &comp->steps.values[op->ch1], last);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            if (ctxt->value
+                && (ctxt->value->type == _XPATH_NODESET)
+                && ctxt->value->nodesetval
+                && (ctxt->value->nodesetval->count >= 1))
+            {
+                /*
+                 * limit tree traversing to first node in the result
+                 */
+                xpath_nodeset_sort(ctxt->value->nodesetval);
+                *last = ctxt->value->nodesetval->nodes[ctxt->value->nodesetval->count - 1];
+            }
+            cur = xpath_comp_op_eval_last(ctxt, &comp->steps.values[op->ch2], last);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+
+            arg2 = xpath_pop_value(ctxt);
+            arg1 = xpath_pop_value(ctxt);
+            if (!arg1 || arg1->type != _XPATH_NODESET ||
+                !arg2 || arg2->type != _XPATH_NODESET)
+            {
+                xpath_object_release(ctxt->context, arg1);
+                xpath_object_release(ctxt->context, arg2);
+                xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+                return 0;
+            }
+            if (ctxt->context->opLimit &&
+                ((arg1->nodesetval && (xpath_check_op_limit(ctxt, arg1->nodesetval->count) < 0))
+                 || (arg2->nodesetval && (xpath_check_op_limit(ctxt, arg2->nodesetval->count) < 0))))
+            {
+                xpath_object_release(ctxt->context, arg1);
+                xpath_object_release(ctxt->context, arg2);
+                break;
+            }
+
+            arg1->nodesetval = xpath_nodeset_merge(ctxt, arg1->nodesetval, arg2->nodesetval);
+            xpath_push_value(ctxt, arg1);
+            xpath_object_release(ctxt->context, arg2);
+            if (total > cur)
+                xpath_comp_swap(op);
+            total += cur;
+            break;
+        case XPATH_OP_ROOT:
+            xpath_root(ctxt);
+            break;
+        case XPATH_OP_NODE:
+            if (op->ch1 != -1)
+                total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            if (op->ch2 != -1)
+                total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch2]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            xpath_push_value(ctxt, xpath_new_nodeset(ctxt, ctxt->context->node));
+            break;
+        case XPATH_OP_COLLECT:
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            total += xpath_node_collect_and_test(ctxt, op, NULL, last, false);
+            break;
+        case XPATH_OP_VALUE:
+            xpath_push_value(ctxt, xpath_object_copy(ctxt, (struct xpath_object *)op->value4));
+            break;
+        case XPATH_OP_SORT:
+            if (op->ch1 != -1)
+                total += xpath_comp_op_eval_last(ctxt, &comp->steps.values[op->ch1], last);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+
+            if (ctxt->value && ctxt->value->type == _XPATH_NODESET)
+                xpath_nodeset_sort(ctxt->value->nodesetval);
+            break;
+        default:
+            total += xpath_comp_op_eval(ctxt, op);
+            break;
+    }
+
+    ctxt->context->depth -= 1;
+    return total;
+}
+
+static void xpath_nodeset_keep_last(struct xpath_nodeset *set)
+{
+    if (!set || set->count <= 1)
+        return;
+
+    set->nodes[0] = set->nodes[set->count - 1];
+    set->count = 1;
+}
+
+static int xpath_evaluate_predicate_result(struct xpath_parser_context *ctxt, struct xpath_object *res)
+{
+    if (!ctxt || !res)
+        return 0;
+
+    switch (res->type)
+    {
+        case _XPATH_BOOLEAN:
+            return res->boolval;
+        case _XPATH_NUMBER:
+            return res->floatval == ctxt->context->proximityPosition;
+        case _XPATH_NODESET:
+            return res->nodesetval && res->nodesetval->count;
+        case _XPATH_STRING:
+            return res->stringval && *res->stringval;
+        default:
+            ;
+    }
+
+    return 0;
+}
+
+static int xpath_comp_op_eval_to_boolean(struct xpath_parser_context *ctxt,
+        struct xpath_step_op *op, int isPredicate)
+{
+    struct xpath_object *resObj = NULL;
+
+start:
+    if (ctxt->context->opLimit && xpath_check_op_limit(ctxt, 1) < 0)
+        return 0;
+
+    switch (op->op)
+    {
+        case XPATH_OP_END:
+            return 0;
+        case XPATH_OP_VALUE:
+            resObj = (struct xpath_object *)op->value4;
+            if (isPredicate)
+                return xpath_evaluate_predicate_result(ctxt, resObj);
+            return xpath_cast_to_boolean(resObj);
+        case XPATH_OP_SORT:
+            /*
+            * We don't need sorting for boolean results. Skip this one.
+            */
+            if (op->ch1 != -1)
+            {
+                op = &ctxt->comp->steps.values[op->ch1];
+                goto start;
+            }
+            return 0;
+        case XPATH_OP_COLLECT:
+            if (op->ch1 == -1)
+                return 0;
+
+            xpath_comp_op_eval(ctxt, &ctxt->comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK)
+                return -1;
+
+            xpath_node_collect_and_test(ctxt, op, NULL, NULL, true);
+            if (ctxt->error != XPATH_EXPRESSION_OK)
+                return -1;
+
+            resObj = xpath_pop_value(ctxt);
+            if (resObj == NULL)
+                return -1;
+            break;
+        default:
+            xpath_comp_op_eval(ctxt, op);
+            if (ctxt->error != XPATH_EXPRESSION_OK)
+            if (ctxt->error != XPATH_EXPRESSION_OK)
+                return -1;
+
+            resObj = xpath_pop_value(ctxt);
+            if (resObj == NULL)
+                return -1;
+            break;
+    }
+
+    if (resObj)
+    {
+        int res;
+
+        if (resObj->type == _XPATH_BOOLEAN)
+        {
+            res = resObj->boolval;
+        }
+        else if (isPredicate)
+        {
+            /*
+            * For predicates a result of type "number" is handled
+            * differently:
+            * SPEC XPath 1.0:
+            * "If the result is a number, the result will be converted
+            *  to true if the number is equal to the context position
+            *  and will be converted to false otherwise;"
+            */
+            res = xpath_evaluate_predicate_result(ctxt, resObj);
+        }
+        else
+        {
+            res = xpath_cast_to_boolean(resObj);
+        }
+        xpath_object_release(ctxt->context, resObj);
+        return res;
+    }
+
+    return 0;
+}
+
+static void xpath_nodeset_clear(struct xpath_nodeset *set, int pos)
+{
+    if (!set || pos >= set->count)
+        return;
+    set->count = pos;
+}
+
+static void xpath_nodeset_filter(struct xpath_parser_context *ctxt, struct xpath_nodeset *set,
+        int filterOpIndex, int minPos, int maxPos, bool hasNsNodes)
+{
+    struct xpath_step_op *filterOp;
+    struct xpath_context *xpctxt;
+    struct domnode *oldnode;
+    int oldcs, oldpp;
+    int i, j, pos;
+
+    if (!set || !set->count)
+        return;
+
+    /*
+    * Check if the node set contains a sufficient number of nodes for
+    * the requested range.
+    */
+    if (set->count < minPos)
+        return xpath_nodeset_clear(set, 0);
+
+    xpctxt = ctxt->context;
+    oldnode = xpctxt->node;
+    oldcs = xpctxt->contextSize;
+    oldpp = xpctxt->proximityPosition;
+    filterOp = &ctxt->comp->steps.values[filterOpIndex];
+
+    xpctxt->contextSize = set->count;
+
+    for (i = 0, j = 0, pos = 1; i < set->count; ++i)
+    {
+        struct domnode *node = set->nodes[i];
+        int res;
+
+        xpctxt->node = node;
+        xpctxt->proximityPosition = i + 1;
+
+        res = xpath_comp_op_eval_to_boolean(ctxt, filterOp, 1);
+
+        if (ctxt->error != XPATH_EXPRESSION_OK)
+            break;
+        if (res < 0)
+        {
+            xpath_parser_context_set_error(ctxt, XPATH_EXPR_ERROR);
+            break;
+        }
+
+        if (res && ((pos >= minPos) && (pos <= maxPos)))
+        {
+            if (i != j)
+            {
+                set->nodes[j] = node;
+                set->nodes[i] = NULL;
+            }
+
+            j += 1;
+        }
+        else
+        {
+            set->nodes[i] = NULL;
+        }
+
+        if (res)
+        {
+            if (pos == maxPos)
+            {
+                i += 1;
+                break;
+            }
+
+            pos += 1;
+        }
+    }
+
+    set->count = j;
+
+    xpctxt->node = oldnode;
+    xpctxt->contextSize = oldcs;
+    xpctxt->proximityPosition = oldpp;
+}
+
+static struct xpath_object * xpath_lookup_variable(struct xpath_context *ctxt, const WCHAR *name, const WCHAR *uri)
+{
+    FIXME("Registering variables is not implemented.\n");
+
+    return NULL;
+}
+
+static xpath_function_ptr xpath_lookup_function(struct xpath_context *ctxt, const WCHAR *name, const WCHAR *uri)
+{
+    for (size_t i = 0; i < ctxt->functions.count; ++i)
+    {
+        if (wcscmp(ctxt->functions.entries[i].name, name)) continue;
+        if (uri && wcscmp(ctxt->functions.entries[i].uri, uri)) continue;
+        return ctxt->functions.entries[i].func;
+    }
+
+    WARN("Unrecognized function %s, uri %s.\n", debugstr_w(name), debugstr_w(uri));
+
+    return NULL;
+}
+
+static int xpath_comp_op_eval(struct xpath_parser_context *ctxt, struct xpath_step_op *op)
+{
+    struct xpath_object *arg1, *arg2;
+    struct xpath_comp_expr *comp;
+    int total = 0, equal, ret;
+
+    if (ctxt->error != XPATH_EXPRESSION_OK)
+        return 0;
+    if (ctxt->context->opLimit && xpath_check_op_limit(ctxt, 1) < 0)
+        return 0;
+    if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_RECURSION_LIMIT_EXCEEDED);
+        return 0;
+    }
+    ctxt->context->depth += 1;
+    comp = ctxt->comp;
+
+    switch (op->op)
+    {
+        case XPATH_OP_END:
+            break;
+        case XPATH_OP_AND:
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            xpath_builtin_boolean(ctxt, 1);
+            if (!ctxt->value || !ctxt->value->boolval)
+                break;
+            arg2 = xpath_pop_value(ctxt);
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch2]);
+            if (ctxt->error)
+            {
+                xpath_free_object(arg2);
+                break;
+            }
+            xpath_builtin_boolean(ctxt, 1);
+            if (ctxt->value)
+                ctxt->value->boolval &= arg2->boolval;
+            xpath_object_release(ctxt->context, arg2);
+            break;
+        case XPATH_OP_OR:
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            xpath_builtin_boolean(ctxt, 1);
+            if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
+                break;
+            arg2 = xpath_pop_value(ctxt);
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch2]);
+            if (ctxt->error)
+            {
+                xpath_free_object(arg2);
+                break;
+            }
+            xpath_builtin_boolean(ctxt, 1);
+            if (ctxt->value)
+                ctxt->value->boolval |= arg2->boolval;
+            xpath_object_release(ctxt->context, arg2);
+            break;
+        case XPATH_OP_EQUAL:
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch2]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            if (op->value)
+                equal = xpath_equal_values(ctxt);
+            else
+                equal = xpath_not_equal_values(ctxt);
+            xpath_push_value(ctxt, xpath_new_boolean(ctxt, equal));
+            break;
+        case XPATH_OP_CMP:
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch2]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            ret = xpath_compare_values(ctxt, op->value, op->value2);
+            xpath_push_value(ctxt, xpath_new_boolean(ctxt, ret));
+            break;
+        case XPATH_OP_PLUS:
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            if (op->ch2 != -1)
+                total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch2]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            if (op->value == 0)
+                xpath_sub_values(ctxt);
+            else if (op->value == 1)
+                xpath_add_values(ctxt);
+            else if (op->value == 2)
+                xpath_value_flip_sign(ctxt);
+            else if (op->value == 3)
+            {
+                xpath_arg_cast_to_number(ctxt);
+                if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+                    return 0;
+            }
+            break;
+        case XPATH_OP_MULT:
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch2]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            if (op->value == 0)
+                xpath_mult_values(ctxt);
+            else if (op->value == 1)
+                xpath_div_values(ctxt);
+            else if (op->value == 2)
+                xpath_mod_values(ctxt);
+            break;
+        case XPATH_OP_UNION:
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch2]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+
+            arg2 = xpath_pop_value(ctxt);
+            arg1 = xpath_pop_value(ctxt);
+            if (!arg1 || arg1->type != _XPATH_NODESET || !arg2 || arg2->type != _XPATH_NODESET)
+            {
+                xpath_object_release(ctxt->context, arg1);
+                xpath_object_release(ctxt->context, arg2);
+                xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+                return 0;
+            }
+            if (ctxt->context->opLimit &&
+                ((arg1->nodesetval && (xpath_check_op_limit(ctxt, arg1->nodesetval->count) < 0))
+                 || (arg2->nodesetval && (xpath_check_op_limit(ctxt, arg2->nodesetval->count) < 0))))
+            {
+                xpath_object_release(ctxt->context, arg1);
+                xpath_object_release(ctxt->context, arg2);
+                break;
+            }
+
+            if (!arg1->nodesetval || (arg2->nodesetval && arg2->nodesetval->count))
+            {
+                arg1->nodesetval = xpath_nodeset_merge(ctxt, arg1->nodesetval, arg2->nodesetval);
+            }
+
+            xpath_push_value(ctxt, arg1);
+            xpath_object_release(ctxt->context, arg2);
+            break;
+        case XPATH_OP_ROOT:
+            xpath_root(ctxt);
+            break;
+        case XPATH_OP_NODE:
+            if (op->ch1 != -1)
+                total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            if (op->ch2 != -1)
+                total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch2]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            xpath_push_value(ctxt, xpath_new_nodeset(ctxt, ctxt->context->node));
+            break;
+        case XPATH_OP_COLLECT:
+            if (op->ch1 == -1)
+                break;
+            total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            total += xpath_node_collect_and_test(ctxt, op, NULL, NULL, 0);
+            break;
+        case XPATH_OP_VALUE:
+            xpath_push_value(ctxt, xpath_object_copy(ctxt, (struct xpath_object *)op->value4));
+            break;
+        case XPATH_OP_VARIABLE:
+            {
+                struct xpath_object *val = NULL;
+
+                if (op->ch1 != -1)
+                    total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+
+                if (op->value5)
+                {
+                    const WCHAR *uri = xpath_ns_lookup(ctxt->context, op->value5);
+
+                    if (!uri)
+                    {
+                        xpath_parser_context_set_error(ctxt, XPATH_UNDEF_PREFIX_ERROR);
+                        break;
+                    }
+
+                    val = xpath_lookup_variable(ctxt->context, op->value4, uri);
+                }
+                else
+                {
+                    val = xpath_lookup_variable(ctxt->context, op->value4, NULL);
+                }
+
+                if (!val)
+                {
+                    xpath_parser_context_set_error(ctxt, XPATH_UNDEF_VARIABLE_ERROR);
+                    return 0;
+                }
+
+                xpath_push_value(ctxt, val);
+                break;
+            }
+        case XPATH_OP_FUNCTION:
+            {
+                const WCHAR *oldFunc, *oldFuncURI;
+                xpath_function_ptr func;
+                int i, frame;
+
+                frame = ctxt->stack.count;
+                if (op->ch1 != -1)
+                {
+                    total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+                    if (ctxt->error != XPATH_EXPRESSION_OK)
+                        break;
+                }
+
+                if (ctxt->stack.count < frame + op->value)
+                {
+                    ctxt->error = XPATH_INVALID_OPERAND;
+                    break;
+                }
+
+                for (i = 0; i < op->value; ++i)
+                {
+                    if (ctxt->stack.values[(ctxt->stack.count - 1) - i] == NULL)
+                    {
+                        ctxt->error = XPATH_INVALID_OPERAND;
+                        break;
+                    }
+                }
+
+                if (op->cache)
+                {
+                    func = op->cache;
+                }
+                else
+                {
+                    const WCHAR *uri = NULL;
+
+                    if (op->value5)
+                    {
+                        uri = xpath_ns_lookup(ctxt->context, op->value5);
+                        if (!uri)
+                        {
+                            xpath_parser_context_set_error(ctxt, XPATH_UNDEF_PREFIX_ERROR);
+                            break;
+                        }
+
+                        func = xpath_lookup_function(ctxt->context, op->value4, uri);
+                    }
+                    else
+                    {
+                        func = xpath_lookup_function(ctxt->context, op->value4, NULL);
+                    }
+
+                    if (!func)
+                    {
+                        xpath_parser_context_set_error(ctxt, XPATH_UNKNOWN_FUNC_ERROR);
+                        return 0;
+                    }
+
+                    op->cache = func;
+                    op->cacheURI = (void *)uri;
+                }
+                oldFunc = ctxt->context->function;
+                oldFuncURI = ctxt->context->functionURI;
+                ctxt->context->function = op->value4;
+                ctxt->context->functionURI = op->cacheURI;
+                func(ctxt, op->value);
+                ctxt->context->function = oldFunc;
+                ctxt->context->functionURI = oldFuncURI;
+                if ((ctxt->error == XPATH_EXPRESSION_OK) && (ctxt->stack.count != frame + 1))
+                {
+                    xpath_parser_context_set_error(ctxt, XPATH_STACK_ERROR);
+                    return 0;
+                }
+                break;
+            }
+        case XPATH_OP_ARG:
+            if (op->ch1 != -1)
+            {
+                total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+                if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            }
+            if (op->ch2 != -1)
+            {
+                total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch2]);
+                if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+            }
+            break;
+        case XPATH_OP_PREDICATE:
+        case XPATH_OP_FILTER:
+            {
+                struct xpath_nodeset *set;
+                struct xpath_object *obj;
+
+                /*
+                 * Optimization for ()[1] selection i.e. the first elem
+                 */
+                if ((op->ch1 != -1) && (op->ch2 != -1) &&
+                    ((comp->steps.values[op->ch1].op == XPATH_OP_SORT) ||
+                     (comp->steps.values[op->ch1].op == XPATH_OP_FILTER)) &&
+                    (comp->steps.values[op->ch2].op == XPATH_OP_VALUE))
+                {
+                    struct xpath_object *val;
+
+                    val = comp->steps.values[op->ch2].value4;
+                    if (val && val->type == _XPATH_NUMBER && val->floatval == 1.0)
+                    {
+                        struct domnode *first = NULL;
+
+                        total += xpath_comp_op_eval_first(ctxt, &comp->steps.values[op->ch1], &first);
+                        if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+                        /*
+                         * The nodeset should be in document order,
+                         * Keep only the first value
+                         */
+                        if (ctxt->value &&
+                            (ctxt->value->type == _XPATH_NODESET) &&
+                             ctxt->value->nodesetval &&
+                            (ctxt->value->nodesetval->count > 1))
+                        {
+                            xpath_nodeset_clear(ctxt->value->nodesetval, 1);
+                        }
+                        break;
+                    }
+                }
+                /*
+                 * Optimization for ()[last()] selection i.e. the last elem
+                 */
+                if ((op->ch1 != -1) && (op->ch2 != -1) &&
+                    (comp->steps.values[op->ch1].op == XPATH_OP_SORT) &&
+                    (comp->steps.values[op->ch2].op == XPATH_OP_SORT))
+                {
+                    int f = comp->steps.values[op->ch2].ch1;
+
+                    if ((f != -1) &&
+                        (comp->steps.values[f].op == XPATH_OP_FUNCTION) &&
+                        (comp->steps.values[f].value5 == NULL) &&
+                        (comp->steps.values[f].value == 0) &&
+                         comp->steps.values[f].value4 &&
+                        !wcscmp(comp->steps.values[f].value4, L"last"))
+                    {
+                        struct domnode *last = NULL;
+
+                        total += xpath_comp_op_eval_last(ctxt, &comp->steps.values[op->ch1], &last);
+                        if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+                        /*
+                         * The nodeset should be in document order,
+                         * Keep only the last value
+                         */
+                        if (ctxt->value &&
+                           (ctxt->value->type == _XPATH_NODESET) &&
+                            ctxt->value->nodesetval &&
+                            ctxt->value->nodesetval->nodes &&
+                           (ctxt->value->nodesetval->count > 1))
+                        {
+                            xpath_nodeset_keep_last(ctxt->value->nodesetval);
+                        }
+                        break;
+                    }
+                }
+                /*
+                * Process inner predicates first.
+                * Example "index[parent::book][1]":
+                *
+                *   PREDICATE   <-- we are here "[1]"
+                *     PREDICATE <-- process "[parent::book]" first
+                *       SORT
+                *         COLLECT  'parent' 'name' 'node' book
+                *           NODE
+                *     ELEM Object is a number : 1
+                */
+                if (op->ch1 != -1)
+                    total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+                if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+                if (op->ch2 == -1)
+                    break;
+                if (ctxt->value == NULL)
+                    break;
+                if (!xpath_check_type(ctxt, _XPATH_NODESET))
+                    return 0;
+                obj = xpath_pop_value(ctxt);
+                set = obj->nodesetval;
+                if (set)
+                    xpath_nodeset_filter(ctxt, set, op->ch2, 1, set->count, true);
+                xpath_push_value(ctxt, obj);
+                break;
+            }
+        case XPATH_OP_SORT:
+            if (op->ch1 != -1)
+                total += xpath_comp_op_eval(ctxt, &comp->steps.values[op->ch1]);
+            if (ctxt->error != XPATH_EXPRESSION_OK) return 0;
+
+            if (ctxt->value && ctxt->value->type == _XPATH_NODESET)
+                xpath_nodeset_sort(ctxt->value->nodesetval);
+            break;
+        default:
+            WARN("Unknown precompiled operation %d.\n", op->op);
+            ctxt->error = XPATH_INVALID_OPERAND;
+            break;
+    }
+
+    ctxt->context->depth -= 1;
+    return total;
+}
+
+static int xpath_run_eval(struct xpath_parser_context *ctxt)
+{
+    struct xpath_comp_expr *comp;
+    int oldDepth;
+
+    if (!ctxt || !ctxt->comp)
+        return -1;
+
+    comp = ctxt->comp;
+    if (comp->last < 0)
+        return -1;
+
+    oldDepth = ctxt->context->depth;
+    xpath_comp_op_eval(ctxt, &comp->steps.values[comp->last]);
+    ctxt->context->depth = oldDepth;
+
+    return 0;
+}
+
+static void xpath_eval_expr(struct xpath_parser_context *ctxt)
+{
+    int oldDepth = 0;
+
+    if (ctxt->context)
+        oldDepth = ctxt->context->depth;
+    xpath_compile_expr(ctxt, true);
+    if (ctxt->context)
+        ctxt->context->depth = oldDepth;
+
+    if (TRACE_ON(msxml_xpath))
+        xpath_debug_dump_comp_expr(ctxt->comp, 0);
+
+    /* Check for trailing characters. */
+    if (*ctxt->cur)
+        return xpath_parser_context_set_error(ctxt, XPATH_EXPR_ERROR);
+
+    /* TODO: optimization pass */
+
+    xpath_run_eval(ctxt);
+}
+
+struct xpath_object * xpath_eval(const WCHAR *str, struct xpath_context *ctx)
+{
+    struct xpath_parser_context *ctxt;
+    struct xpath_object *res = NULL;
+
+    if (!(ctxt = xpath_create_parser_context(str, ctx)))
+        return NULL;
+    xpath_eval_expr(ctxt);
+
+    if (ctxt->error == XPATH_EXPRESSION_OK)
+    {
+        res = xpath_pop_value(ctxt);
+        if (res == NULL)
+            ERR("No results\n");
+        else if (ctxt->stack.count)
+            ERR("Stack not empty\n");
+    }
+
+    xpath_free_parser_context(ctxt);
+    return res;
+}
+
+static void xpath_register_func_uri(struct xpath_context *ctxt, const WCHAR *name,
+        const WCHAR *uri, xpath_function_ptr func)
+{
+    struct xpath_function *entry;
+
+    if (!array_reserve((void **)&ctxt->functions.entries, &ctxt->functions.capacity,
+            ctxt->functions.count + 1, sizeof(*ctxt->functions.entries)))
+    {
+        return;
+    }
+
+    entry = &ctxt->functions.entries[ctxt->functions.count++];
+    entry->name = wcsdup(name);
+    entry->uri = wcsdup(uri);
+    entry->func = func;
+}
+
+static void xpath_register_func(struct xpath_context *ctxt, const WCHAR *name, xpath_function f)
+{
+    return xpath_register_func_uri(ctxt, name, NULL, f);
+}
+
+static double xpath_string_eval_number(const WCHAR *str)
+{
+    bool isneg = false, is_exponent_negative = false, ok = false;
+    const WCHAR *cur = str;
+    int exponent = 0;
+    double ret;
+
+    if (!cur) return 0.0;
+
+    while (xml_is_space(*cur))
+        ++cur;
+
+    if (*cur == '-')
+    {
+        isneg = 1;
+        ++cur;
+    }
+    if ((*cur != '.') && ((*cur < '0') || (*cur > '9')))
+        return NAN;
+
+    ret = 0;
+    while ((*cur >= '0') && (*cur <= '9'))
+    {
+        ret = ret * 10 + (*cur - '0');
+        ok = true;
+        ++cur;
+    }
+
+    if (*cur == '.')
+    {
+        int v, frac = 0, max;
+        double fraction = 0;
+
+        ++cur;
+        if (((*cur < '0') || (*cur > '9')) && !ok)
+            return NAN;
+
+        while (*cur == '0')
+        {
+            frac = frac + 1;
+            ++cur;
+        }
+        max = frac + XPATH_MAX_FRAC;
+        while (((*cur >= '0') && (*cur <= '9')) && (frac < max))
+        {
+            v = (*cur - '0');
+            fraction = fraction * 10 + v;
+            frac = frac + 1;
+            ++cur;
+        }
+        fraction /= pow(10.0, frac);
+        ret = ret + fraction;
+        while ((*cur >= '0') && (*cur <= '9'))
+            ++cur;
+    }
+
+    if (*cur == 'e' || *cur == 'E')
+    {
+        ++cur;
+        if (*cur == '-')
+        {
+            is_exponent_negative = true;
+            ++cur;
+        }
+        else if (*cur == '+')
+        {
+            ++cur;
+        }
+        while (*cur >= '0' && *cur <= '9')
+        {
+            if (exponent < 1000000)
+                exponent = exponent * 10 + (*cur - '0');
+            ++cur;
+        }
+    }
+    while (xml_is_space(*cur))
+        ++cur;
+
+    if (*cur) return NAN;
+    if (isneg) ret = -ret;
+    if (is_exponent_negative) exponent = -exponent;
+    ret *= pow(10.0, (double)exponent);
+    return ret;
+}
+
+static double xpath_cast_string_to_number(const WCHAR *val)
+{
+    return xpath_string_eval_number(val);
+}
+
+static double xpath_cast_node_to_number(struct domnode *node)
+{
+    WCHAR *strval;
+    double ret;
+
+    if (!node)
+        return NAN;
+
+    strval = xpath_cast_node_to_string(node);
+    if (!strval)
+        return NAN;
+
+    ret = xpath_string_eval_number(strval);
+    free(strval);
+
+    return ret;
+}
+
+static double xpath_cast_boolean_to_number(int val)
+{
+    return val ? 1.0 : 0.0;
+}
+
+static double xpath_cast_to_number(struct xpath_object *val)
+{
+    if (!val)
+        return NAN;
+
+    switch (val->type)
+    {
+    case XPATH_NODESET:
+        return xpath_cast_nodeset_to_number(val->nodesetval);
+    case XPATH_STRING:
+        return xpath_cast_string_to_number(val->stringval);
+    case XPATH_NUMBER:
+        return val->floatval;
+    case XPATH_BOOLEAN:
+        return xpath_cast_boolean_to_number(val->boolval);
+    default:
+        return NAN;
+    }
+}
+
+static WCHAR * xpath_cast_boolean_to_string(int val)
+{
+    return wcsdup(val ? L"true" : L"false");
+}
+
+static void xpath_format_number(double number, WCHAR *buffer, int buffersize)
+{
+    if ((number > INT_MIN) && (number < INT_MAX) && (number == (int) number))
+    {
+        int value = (int) number;
+        WCHAR *ptr, *cur;
+        WCHAR work[30];
+
+        ptr = buffer;
+        if (value == 0)
+        {
+            *ptr++ = '0';
+        }
+        else
+        {
+            _snwprintf(work, 29, L"%d", value);
+            cur = work;
+            while ((*cur) && (ptr - buffer < buffersize))
+                *ptr++ = *cur++;
+        }
+        if (ptr - buffer < buffersize)
+        {
+            *ptr = 0;
+        }
+        else if (buffersize > 0)
+        {
+            ptr--;
+            *ptr = 0;
+        }
+    }
+    else
+    {
+        /*
+          For the dimension of work,
+              DBL_DIG is number of significant digits
+              EXPONENT is only needed for "scientific notation"
+              3 is sign, decimal point, and terminating zero
+          LOWER_DOUBLE_EXP is max number of leading zeroes in fraction
+          Note that this dimension is slightly (a few characters)
+          larger than actually necessary.
+        */
+        WCHAR work[XPATH_DBL_DIG + XPATH_EXPONENT_DIGITS + 3 + XPATH_LOWER_DOUBLE_EXP];
+        int integer_place, fraction_place;
+        WCHAR *ptr, *after_fraction;
+        double absolute_value;
+        int size;
+
+        absolute_value = fabs(number);
+
+        /*
+         * First choose format - scientific or regular floating point.
+         * In either case, result is in work, and after_fraction points
+         * just past the fractional part.
+        */
+        if (((absolute_value > XPATH_UPPER_DOUBLE) || (absolute_value < XPATH_LOWER_DOUBLE)) && (absolute_value != 0.0))
+        {
+            /* Use scientific notation */
+            integer_place = XPATH_DBL_DIG + XPATH_EXPONENT_DIGITS + 1;
+            fraction_place = XPATH_DBL_DIG - 1;
+            size = _snwprintf(work, ARRAYSIZE(work), L"%*.*e", integer_place, fraction_place, number);
+            while ((size > 0) && (work[size] != 'e')) size--;
+        }
+        else
+        {
+            /* Use regular notation */
+            if (absolute_value > 0.0)
+            {
+                integer_place = (int)log10(absolute_value);
+                if (integer_place > 0)
+                    fraction_place = XPATH_DBL_DIG - integer_place - 1;
+                else
+                    fraction_place = XPATH_DBL_DIG - integer_place;
+            }
+            else
+            {
+                fraction_place = 1;
+            }
+            size = _snwprintf(work, ARRAYSIZE(work), L"%0.*f", fraction_place, number);
+        }
+
+        /* Remove leading spaces sometimes inserted by snprintf */
+        while (work[0] == ' ')
+        {
+            for (ptr = work; *ptr; ++ptr)
+                ptr[0] = ptr[1];
+            size--;
+        }
+
+        /* Remove fractional trailing zeroes */
+        after_fraction = work + size;
+        ptr = after_fraction;
+        while (*(--ptr) == '0')
+            ;
+        if (*ptr != '.')
+            ptr++;
+        while ((*ptr++ = *after_fraction++) != 0);
+
+        /* Finally copy result back to caller */
+        size = wcslen(work) + 1;
+        if (size > buffersize)
+             size = buffersize;
+        memmove(buffer, work, size);
+    }
+}
+
+static WCHAR * xpath_cast_number_to_string(double val)
+{
+    WCHAR *ret;
+
+    switch (xpath_isinf(val))
+    {
+        case 1:
+            return wcsdup(L"Infinity");
+        case -1:
+            return wcsdup(L"-Infinity");
+        default:
+            ;
+    }
+
+    if (isnan(val))
+    {
+        return wcsdup(L"NaN");
+    }
+    else if (val == 0)
+    {
+        /* Omit sign for negative zero. */
+        ret = wcsdup(L"0");
+    }
+    else
+    {
+        WCHAR buf[100];
+        xpath_format_number(val, buf, 99);
+        buf[99] = 0;
+
+        ret = wcsdup(buf);
+    }
+
+    return ret;
+}
+
+static WCHAR * xpath_cast_to_string(struct xpath_object *val)
+{
+    if (!val)
+        return wcsdup(L"");
+
+    switch (val->type)
+    {
+        case XPATH_NODESET:
+            return xpath_cast_nodeset_to_string(val->nodesetval);
+        case XPATH_STRING:
+            return wcsdup(val->stringval);
+        case XPATH_BOOLEAN:
+            return xpath_cast_boolean_to_string(val->boolval);
+        case XPATH_NUMBER:
+            return xpath_cast_number_to_string(val->floatval);
+        default:
+            return wcsdup(L"");
+    }
+}
+
+static struct xpath_object * xpath_convert_number(struct xpath_parser_context *ctxt, struct xpath_object *val)
+{
+    struct xpath_object *ret;
+
+    if (!val)
+        return xpath_new_number(ctxt, 0.0);
+
+    if (val->type == _XPATH_NUMBER)
+        return val;
+
+    ret = xpath_new_number(ctxt, xpath_cast_to_number(val));
+    xpath_free_object(val);
+    return ret;
+}
+
+static void xpath_builtin_number(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *cur;
+    double res;
+
+    if (nargs == 0)
+    {
+        if (ctxt->context->node)
+        {
+            xpath_push_value(ctxt, xpath_new_number(ctxt, 0.0));
+        }
+        else
+        {
+            WCHAR *content = xpath_cast_node_to_string(ctxt->context->node);
+
+            res = xpath_string_eval_number(content);
+            xpath_push_value(ctxt, xpath_new_number(ctxt, res));
+            free(content);
+        }
+
+        return;
+    }
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+
+    cur = xpath_pop_value(ctxt);
+    xpath_push_value(ctxt, xpath_convert_number(ctxt, cur));
+}
+
+static void xpath_arg_cast_to_boolean(struct xpath_parser_context *ctxt)
+{
+    if (ctxt->value && ctxt->value->type != _XPATH_NUMBER)
+        xpath_builtin_boolean(ctxt, 1);
+}
+
+static void xpath_builtin_ceiling(struct xpath_parser_context *ctxt, int nargs)
+{
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+
+    xpath_arg_cast_to_number(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+        return;
+
+    ctxt->value->floatval = ceil(ctxt->value->floatval);
+}
+
+static void xpath_builtin_count(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *cur;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+
+    if (!ctxt->value || (ctxt->value->type != _XPATH_NODESET))
+    {
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+    }
+
+    cur = xpath_pop_value(ctxt);
+    if (!cur || !cur->nodesetval)
+        xpath_push_value(ctxt, xpath_new_number(ctxt, 0.0));
+    else
+        xpath_push_value(ctxt, xpath_new_number(ctxt, cur->nodesetval->count));
+    xpath_free_object(cur);
+}
+
+static struct xpath_object * xpath_convert_string(struct xpath_parser_context *ctxt, struct xpath_object *val)
+{
+    WCHAR *res = NULL;
+
+    if (!val)
+        return xpath_new_string(ctxt, L"");
+
+    switch (val->type)
+    {
+        case XPATH_NODESET:
+            res = xpath_cast_nodeset_to_string(val->nodesetval);
+            break;
+        case XPATH_STRING:
+            return val;
+        case XPATH_BOOLEAN:
+            res = xpath_cast_boolean_to_string(val->boolval);
+            break;
+        case XPATH_NUMBER:
+            res = xpath_cast_number_to_string(val->floatval);
+            break;
+        default:
+            ;
+    }
+
+    xpath_free_object(val);
+    if (!res)
+        return xpath_new_string(ctxt, L"");
+
+    return xpath_wrap_string(ctxt, res);
+}
+
+static void xpath_builtin_string(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *cur;
+
+    if (!ctxt)
+        return;
+
+    if (!nargs)
+    {
+        xpath_push_value(ctxt, xpath_wrap_string(ctxt, xpath_cast_node_to_string(ctxt->context->node)));
+        return;
+    }
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+
+    cur = xpath_pop_value(ctxt);
+    if (!cur)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+
+    xpath_push_value(ctxt, xpath_convert_string(ctxt, cur));
+}
+
+static void xpath_arg_cast_to_string(struct xpath_parser_context *ctxt)
+{
+    if (ctxt->value && ctxt->value->type != _XPATH_STRING)
+        xpath_builtin_string(ctxt, 1);
+}
+
+static WCHAR *xpath_strcat(struct xpath_parser_context *ctxt, const WCHAR *str1, const WCHAR *str2)
+{
+    size_t len1 = wcslen(str1);
+    size_t len2 = wcslen(str2);
+    WCHAR *ret;
+
+    ret = xpath_malloc(ctxt, (len1 + len2 + 1) * sizeof(*str1));
+    if (ret)
+    {
+        memcpy(ret, str1, len1 * sizeof(*str1));
+        memcpy(ret + len1, str2, len2 * sizeof(*str1));
+        ret[len1 + len2] = 0;
+    }
+
+    return ret;
+}
+
+static void xpath_builtin_concat(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *cur, *newobj;
+    WCHAR *tmp;
+
+    if (ctxt)
+        return;
+
+    if (nargs < 2)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_ARITY);
+
+    xpath_arg_cast_to_string(ctxt);
+    cur = xpath_pop_value(ctxt);
+    if (!cur || cur->type != _XPATH_STRING)
+    {
+        xpath_object_release(ctxt->context, cur);
+        return;
+    }
+    nargs--;
+
+    while (nargs > 0)
+    {
+        xpath_arg_cast_to_string(ctxt);
+        newobj = xpath_pop_value(ctxt);
+        if (!newobj || newobj->type != _XPATH_STRING)
+        {
+            xpath_object_release(ctxt->context, newobj);
+            xpath_object_release(ctxt->context, cur);
+            return xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+        }
+
+        tmp = xpath_strcat(ctxt, newobj->stringval, cur->stringval);
+        newobj->stringval = cur->stringval;
+        cur->stringval = tmp;
+
+        xpath_object_release(ctxt->context, newobj);
+        nargs--;
+    }
+    xpath_push_value(ctxt, cur);
+}
+
+static void xpath_builtin_contains(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *hay, *needle;
+    bool match;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 2))
+        return;
+
+    xpath_arg_cast_to_string(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_STRING))
+        return;
+    needle = xpath_pop_value(ctxt);
+    xpath_arg_cast_to_string(ctxt);
+    hay = xpath_pop_value(ctxt);
+
+    if (hay && hay->type == _XPATH_STRING)
+    {
+        match = wcsstr(hay->stringval, needle->stringval);
+        xpath_push_value(ctxt, xpath_new_boolean(ctxt, match));
+        xpath_object_release(ctxt->context, hay);
+        xpath_object_release(ctxt->context, needle);
+    }
+    else
+    {
+        xpath_object_release(ctxt->context, hay);
+        xpath_object_release(ctxt->context, needle);
+        xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+    }
+}
+
+static void xpath_builtin_false(struct xpath_parser_context *ctxt, int nargs)
+{
+    if (!xpath_builtin_check_stack(ctxt, nargs, 0))
+        return;
+    xpath_push_value(ctxt, xpath_new_boolean(ctxt, 0));
+}
+
+static void xpath_builtin_floor(struct xpath_parser_context *ctxt, int nargs)
+{
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+    xpath_arg_cast_to_number(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+        return;
+
+    ctxt->value->floatval = floor(ctxt->value->floatval);
+}
+
+static struct xpath_nodeset * xpath_get_elements_by_id(struct domnode *root, const WCHAR *ids)
+{
+    FIXME("Id set is not supported.\n");
+
+    return NULL;
+}
+
+static void xpath_builtin_id(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_nodeset *ret;
+    struct xpath_object *obj;
+    WCHAR *tokens;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+    obj = xpath_pop_value(ctxt);
+    if (!obj)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+
+    if (obj->type == _XPATH_NODESET)
+    {
+        struct xpath_nodeset *ns;
+
+        ret = xpath_create_nodeset(ctxt, NULL);
+
+        if (obj->nodesetval)
+        {
+            for (int i = 0; i < obj->nodesetval->count; ++i)
+            {
+                tokens = xpath_cast_node_to_string(obj->nodesetval->nodes[i]);
+                ns = xpath_get_elements_by_id(ctxt->context->root, tokens);
+
+                ret = xpath_nodeset_merge(ctxt, ret, ns);
+                xpath_free_nodeset(ns);
+                free(tokens);
+            }
+        }
+        xpath_object_release(ctxt->context, obj);
+        xpath_push_value(ctxt, xpath_wrap_nodeset(ctxt, ret));
+        return;
+    }
+    obj = xpath_convert_string(ctxt, obj);
+    if (obj) return;
+
+    ret = xpath_get_elements_by_id(ctxt->context->root, obj->stringval);
+    xpath_push_value(ctxt, xpath_wrap_nodeset(ctxt, ret));
+
+    xpath_object_release(ctxt->context, obj);
+    return;
+}
+
+static void xpath_builtin_last(struct xpath_parser_context *ctxt, int nargs)
+{
+    if (!xpath_builtin_check_stack(ctxt, nargs, 0))
+        return;
+
+    if (ctxt->context->contextSize >= 0)
+    {
+        xpath_push_value(ctxt, xpath_new_number(ctxt, ctxt->context->contextSize));
+    }
+    else
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_INVALID_CTXT_SIZE);
+    }
+}
+
+static WCHAR * xpath_node_get_lang(struct domnode *node)
+{
+    WCHAR *lang;
+    VARIANT v;
+
+    if (!node)
+        return NULL;
+
+    while (node)
+    {
+        if (node_get_attribute_value(node, L"xml:lang", &v) == S_OK)
+        {
+            lang = wcsdup(V_BSTR(&v));
+            VariantClear(&v);
+            return lang;
+        }
+
+        node = xpath_get_parent(node);
+    }
+
+    return NULL;
+}
+
+static void xpath_builtin_lang(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *val = NULL;
+    const WCHAR *lang;
+    WCHAR *nodelang;
+    int ret = 0;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+
+    xpath_arg_cast_to_string(ctxt);
+    if (!xpath_check_type(ctxt, XPATH_STRING))
+        return;
+
+    val = xpath_pop_value(ctxt);
+    lang = val->stringval;
+
+    nodelang = xpath_node_get_lang(ctxt->context->node);
+    if (nodelang && lang)
+        ret = !wcsicmp(nodelang, lang);
+
+    free(nodelang);
+
+    xpath_object_release(ctxt->context, val);
+    xpath_push_value(ctxt, xpath_new_boolean(ctxt, ret));
+}
+
+static void xpath_builtin_not(struct xpath_parser_context *ctxt, int nargs)
+{
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+
+    xpath_arg_cast_to_boolean(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_BOOLEAN))
+        return;
+
+    ctxt->value->boolval = !ctxt->value->boolval;
+}
+
+static void xpath_builtin_normalize_space(struct xpath_parser_context *ctxt, int nargs)
+{
+    WCHAR *source, *target;
+    bool blank;
+
+    if (!ctxt)
+        return;
+
+    if (!nargs)
+    {
+        /* Use current context node */
+        xpath_push_value(ctxt, xpath_wrap_string(ctxt, xpath_cast_node_to_string(ctxt->context->node)));
+        nargs = 1;
+    }
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+    xpath_arg_cast_to_string(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_STRING))
+        return;
+    source = ctxt->value->stringval;
+    if (!source)
+        return;
+    target = source;
+
+    /* Skip leading whitespaces */
+    while (xml_is_space(*source))
+        source++;
+
+    /* Collapse intermediate whitespaces, and skip trailing whitespaces */
+    blank = false;
+    while (*source)
+    {
+        if (xml_is_space(*source))
+        {
+            blank = true;
+        }
+        else
+        {
+            if (blank)
+            {
+                *target++ = 0x20;
+                blank = false;
+            }
+            *target++ = *source;
+        }
+        source++;
+    }
+    *target = 0;
+}
+
+static void xpath_builtin_local_name(struct xpath_parser_context *ctxt, int nargs)
+{
+    const struct domnode *node;
+    struct xpath_object *cur;
+
+    if (!ctxt)
+        return;
+
+    if (nargs == 0)
+    {
+        xpath_push_value(ctxt, xpath_new_nodeset(ctxt, ctxt->context->node));
+        nargs = 1;
+    }
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+    if (!ctxt->value || ctxt->value->type != _XPATH_NODESET)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+
+    cur = xpath_pop_value(ctxt);
+    if (!cur->nodesetval || cur->nodesetval->count == 0)
+    {
+        xpath_push_value(ctxt, xpath_new_string(ctxt, L""));
+    }
+    else
+    {
+        node = cur->nodesetval->nodes[0];
+        switch (node->type)
+        {
+            case NODE_ELEMENT:
+            case NODE_ATTRIBUTE:
+            case NODE_PROCESSING_INSTRUCTION:
+                xpath_push_value(ctxt, xpath_new_string(ctxt, node->name));
+                break;
+            default:
+                xpath_push_value(ctxt, xpath_new_string(ctxt, L""));
+       }
+    }
+    xpath_object_release(ctxt->context, cur);
+}
+
+static void xpath_builtin_name(struct xpath_parser_context *ctxt, int nargs)
+{
+    const struct domnode *node;
+    struct xpath_object *cur;
+
+    if (nargs == 0)
+    {
+        xpath_push_value(ctxt, xpath_new_nodeset(ctxt, ctxt->context->node));
+        nargs = 1;
+    }
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+
+    if (!ctxt->value || ctxt->value->type != _XPATH_NODESET)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+
+    cur = xpath_pop_value(ctxt);
+    if (!cur->nodesetval || cur->nodesetval->count == 0)
+    {
+        xpath_push_value(ctxt, xpath_new_string(ctxt, L""));
+    }
+    else
+    {
+        node = cur->nodesetval->nodes[0];
+        switch (node->type)
+        {
+            case NODE_ELEMENT:
+            case NODE_ATTRIBUTE:
+                xpath_push_value(ctxt, xpath_new_string(ctxt, node->qname));
+                break;
+            default:
+                xpath_push_value(ctxt, xpath_new_string(ctxt, node->name));
+                break;
+        }
+    }
+
+    xpath_object_release(ctxt->context, cur);
+}
+
+static void xpath_builtin_namespace_uri(struct xpath_parser_context *ctxt, int nargs)
+{
+    const struct domnode *node;
+    struct xpath_object *cur;
+
+    if (!ctxt) return;
+
+    if (nargs == 0)
+    {
+        xpath_push_value(ctxt, xpath_new_nodeset(ctxt, ctxt->context->node));
+        nargs = 1;
+    }
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+
+    if (!ctxt->value || ctxt->value->type != _XPATH_NODESET)
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+
+    cur = xpath_pop_value(ctxt);
+    if (!cur->nodesetval || cur->nodesetval->count == 0)
+    {
+        xpath_push_value(ctxt, xpath_new_string(ctxt, L""));
+    }
+    else
+    {
+        node = cur->nodesetval->nodes[0];
+        switch (node->type)
+        {
+            case NODE_ELEMENT:
+            case NODE_ATTRIBUTE:
+                if (node->uri)
+                    xpath_push_value(ctxt, xpath_new_string(ctxt, node->uri));
+                else
+                    xpath_push_value(ctxt, xpath_new_string(ctxt, L""));
+                break;
+            default:
+                xpath_push_value(ctxt, xpath_new_string(ctxt, L""));
+        }
+    }
+
+    xpath_object_release(ctxt->context, cur);
+}
+
+static void xpath_builtin_position(struct xpath_parser_context *ctxt, int nargs)
+{
+    if (!xpath_builtin_check_stack(ctxt, nargs, 0))
+        return;
+
+    if (ctxt->context->proximityPosition >= 0)
+    {
+        xpath_push_value(ctxt, xpath_new_number(ctxt, ctxt->context->proximityPosition));
+    }
+    else
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_INVALID_CTXT_POSITION);
+    }
+}
+
+static void xpath_builtin_round(struct xpath_parser_context *ctxt, int nargs)
+{
+    double f;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 1))
+        return;
+    xpath_arg_cast_to_number(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+        return;
+
+    f = ctxt->value->floatval;
+
+    if ((f >= -0.5) && (f < 0.5))
+    {
+        /* Handles negative zero. */
+        ctxt->value->floatval *= 0.0;
+    }
+    else
+    {
+        double rounded = floor(f);
+        if (f - rounded >= 0.5)
+            rounded += 1.0;
+        ctxt->value->floatval = rounded;
+    }
+}
+
+static void xpath_builtin_string_length(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *cur;
+
+    if (nargs == 0)
+    {
+        if (!ctxt || !ctxt->context)
+            return;
+        if (!ctxt->context->node)
+        {
+            xpath_push_value(ctxt, xpath_new_number(ctxt, 0.0));
+        }
+        else
+        {
+            WCHAR *content;
+
+            content = xpath_cast_node_to_string(ctxt->context->node);
+            xpath_push_value(ctxt, xpath_new_number(ctxt, wcslen(content)));
+            free(content);
+        }
+
+        return;
+    }
+    if (!xpath_builtin_check_stack(ctxt, nargs, 0))
+        return;
+    xpath_arg_cast_to_number(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_STRING))
+        return;
+    cur = xpath_pop_value(ctxt);
+    xpath_push_value(ctxt, xpath_new_number(ctxt, wcslen(cur->stringval)));
+    xpath_object_release(ctxt->context, cur);
+}
+
+static void xpath_builtin_startswith(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *hay, *needle;
+    int n, match;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 2))
+        return;
+    xpath_arg_cast_to_string(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_STRING))
+        return;
+    needle = xpath_pop_value(ctxt);
+    xpath_arg_cast_to_string(ctxt);
+    hay = xpath_pop_value(ctxt);
+
+    if (!hay || (hay->type != _XPATH_STRING))
+    {
+        xpath_object_release(ctxt->context, hay);
+        xpath_object_release(ctxt->context, needle);
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+    }
+    n = wcslen(needle->stringval);
+    match = !wcsncmp(hay->stringval, needle->stringval, n);
+    xpath_push_value(ctxt, xpath_new_boolean(ctxt, !!match));
+    xpath_object_release(ctxt->context, hay);
+    xpath_object_release(ctxt->context, needle);
+}
+
+static struct xpath_object * xpath_substring(struct xpath_parser_context *ctxt, WCHAR *str, int start, int count)
+{
+    int len = wcslen(str);
+    WCHAR *res;
+
+    if (start >= len || count == 0)
+        return xpath_new_string(ctxt, L"");
+
+    len = min(len - start, count);
+    res = xpath_malloc(ctxt, (len + 1) * sizeof(WCHAR));
+    if (res)
+    {
+        memcpy(res, str + start, len * sizeof(WCHAR));
+        res[len] = 0;
+    }
+
+    return xpath_wrap_string(ctxt, res);
+}
+
+static void xpath_builtin_substring(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *str, *start, *len;
+    int i = 1, j = INT_MAX;
+    double le = 0, in;
+
+    if (nargs < 2 || nargs > 3)
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_INVALID_ARITY);
+        return;
+    }
+
+    /*
+     * take care of possible last (position) argument
+    */
+    if (nargs == 3)
+    {
+        xpath_arg_cast_to_number(ctxt);
+        if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+            return;
+        len = xpath_pop_value(ctxt);
+        le = len->floatval;
+        xpath_object_release(ctxt->context, len);
+    }
+
+    xpath_arg_cast_to_number(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_NUMBER))
+        return;
+    start = xpath_pop_value(ctxt);
+    in = start->floatval;
+    xpath_object_release(ctxt->context, start);
+    xpath_arg_cast_to_string(ctxt);
+    if (!xpath_check_type(ctxt, _XPATH_STRING))
+        return;
+    str = xpath_pop_value(ctxt);
+
+    /* Logical NOT to handle NaNs */
+    if (!(in < INT_MAX))
+    {
+        i = INT_MAX;
+    }
+    else if (in >= 1.0)
+    {
+        i = (int)in;
+        if (in - floor(in) >= 0.5)
+            i += 1;
+    }
+
+    if (nargs == 3)
+    {
+        double rin, rle, end;
+
+        rin = floor(in);
+        if (in - rin >= 0.5)
+            rin += 1.0;
+
+        rle = floor(le);
+        if (le - rle >= 0.5)
+            rle += 1.0;
+
+        end = rin + rle;
+        if (!(end >= 1.0)) /* Logical NOT to handle NaNs */
+            j = 1;
+        else if (end < INT_MAX)
+            j = (int)end;
+    }
+
+    if (i < j)
+        xpath_push_value(ctxt, xpath_substring(ctxt, str->stringval, i - 1, j - 1));
+    else
+        xpath_push_value(ctxt, xpath_new_string(ctxt, L""));
+
+    xpath_object_release(ctxt->context, str);
+}
+
+static void xpath_builtin_substringbefore(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *str, *find;
+    WCHAR *target = NULL;
+    const WCHAR *point;
+    int offset;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 2))
+        return;
+    xpath_arg_cast_to_string(ctxt);
+    find = xpath_pop_value(ctxt);
+    xpath_arg_cast_to_string(ctxt);
+    str = xpath_pop_value(ctxt);
+
+    point = wcsstr(str->stringval, find->stringval);
+    if (point)
+    {
+        offset = point - str->stringval;
+        target = xpath_strndup(ctxt, str->stringval, offset);
+    }
+    xpath_push_value(ctxt, xpath_new_string(ctxt, target ? target : L""));
+    free(target);
+
+    xpath_object_release(ctxt->context, str);
+    xpath_object_release(ctxt->context, find);
+}
+
+static void xpath_builtin_substringafter(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *str, *find;
+    WCHAR *target = NULL;
+    const WCHAR *point;
+    int offset;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 2))
+        return;
+    xpath_arg_cast_to_string(ctxt);
+    find = xpath_pop_value(ctxt);
+    xpath_arg_cast_to_string(ctxt);
+    str = xpath_pop_value(ctxt);
+
+    point = wcsstr(str->stringval, find->stringval);
+    if (point)
+    {
+        offset = point - str->stringval + wcslen(find->stringval);
+        target = xpath_strndup(ctxt, str->stringval + offset, wcslen(str->stringval) - offset);
+    }
+    xpath_push_value(ctxt, xpath_new_string(ctxt, target ? target : L""));
+    free(target);
+
+    xpath_object_release(ctxt->context, str);
+    xpath_object_release(ctxt->context, find);
+}
+
+static void xpath_builtin_sum(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *cur;
+    double res = 0.0;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 2))
+        return;
+    if (!ctxt->value || (ctxt->value->type != _XPATH_NODESET))
+    {
+        return xpath_parser_context_set_error(ctxt, XPATH_INVALID_TYPE);
+    }
+    cur = xpath_pop_value(ctxt);
+
+    if (cur->nodesetval)
+    {
+        for (int i = 0; i < cur->nodesetval->count; ++i)
+            res += xpath_cast_node_to_number(cur->nodesetval->nodes[i]);
+    }
+    xpath_push_value(ctxt, xpath_new_number(ctxt, res));
+    xpath_object_release(ctxt->context, cur);
+}
+
+static void xpath_builtin_true(struct xpath_parser_context *ctxt, int nargs)
+{
+    if (!xpath_builtin_check_stack(ctxt, nargs, 0))
+        return;
+    xpath_push_value(ctxt, xpath_new_boolean(ctxt, 1));
+}
+
+static void xpath_builtin_translate(struct xpath_parser_context *ctxt, int nargs)
+{
+    struct xpath_object *str, *from, *to;
+    WCHAR *target;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 3))
+        return;
+
+    xpath_arg_cast_to_string(ctxt);
+    to = xpath_pop_value(ctxt);
+    xpath_arg_cast_to_string(ctxt);
+    from = xpath_pop_value(ctxt);
+    xpath_arg_cast_to_string(ctxt);
+    str = xpath_pop_value(ctxt);
+
+    target = xpath_translate_function(to->stringval, from->stringval, str->stringval);
+    xpath_push_value(ctxt, xpath_wrap_string(ctxt, target));
+
+    xpath_object_release(ctxt->context, str);
+    xpath_object_release(ctxt->context, from);
+    xpath_object_release(ctxt->context, to);
+}
+
+static double xpath_pop_number(struct xpath_parser_context *ctxt)
+{
+    struct xpath_object *obj;
+    double ret;
+
+    obj = xpath_pop_value(ctxt);
+    if (!obj)
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+        return 0.0;
+    }
+
+    if (obj->type != _XPATH_NUMBER)
+        ret = xpath_cast_to_number(obj);
+    else
+        ret = obj->floatval;
+
+    xpath_object_release(ctxt->context, obj);
+    return ret;
+}
+
+static WCHAR * xpath_pop_string(struct xpath_parser_context *ctxt)
+{
+    struct xpath_object *obj;
+    WCHAR *ret;
+
+    obj = xpath_pop_value(ctxt);
+    if (!obj)
+    {
+        xpath_parser_context_set_error(ctxt, XPATH_INVALID_OPERAND);
+        return NULL;
+    }
+
+    ret = xpath_cast_to_string(obj);
+    if (obj->stringval == ret)
+        obj->stringval = NULL;
+
+    xpath_object_release(ctxt->context, obj);
+    return ret;
+}
+
+/* XSLPattern translated functions */
+
+/* Indexing is 0-based in XSLPattern */
+static void xpath_builtin_index(struct xpath_parser_context *ctxt, int nargs)
+{
+    if (!xpath_builtin_check_stack(ctxt, nargs, 0))
+        return;
+
+    xpath_builtin_position(ctxt, 0);
+    xpath_push_value(ctxt, xpath_new_number(ctxt, xpath_pop_number(ctxt) - 1.0));
+}
+
+/* Boolean end() = pos == last */
+static void xpath_builtin_end(struct xpath_parser_context *ctxt, int nargs)
+{
+    double pos, last;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 0))
+        return;
+
+    xpath_builtin_position(ctxt, 0);
+    pos = xpath_pop_number(ctxt);
+    xpath_builtin_last(ctxt, 0);
+    last = xpath_pop_number(ctxt);
+    xpath_push_value(ctxt, xpath_new_boolean(ctxt, pos == last));
+}
+
+/* Returns numeric value for context node type. */
+static void xpath_builtin_node_type(struct xpath_parser_context *ctxt, int nargs)
+{
+    if (!xpath_builtin_check_stack(ctxt, nargs, 0))
+        return;
+
+    xpath_push_value(ctxt, xpath_new_number(ctxt, ctxt->context->node->type));
+}
+
+static void xpath_builtin_ieq(struct xpath_parser_context *ctxt, int nargs)
+{
+    WCHAR *arg1, *arg2;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 2))
+        return;
+
+    arg2 = xpath_pop_string(ctxt);
+    arg1 = xpath_pop_string(ctxt);
+
+    xpath_push_value(ctxt, xpath_new_boolean(ctxt, wcsicmp(arg1, arg2) == 0));
+
+    free(arg1);
+    free(arg2);
+}
+
+static void xpath_builtin_ineq(struct xpath_parser_context *ctxt, int nargs)
+{
+    WCHAR *arg1, *arg2;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 2))
+        return;
+
+    arg2 = xpath_pop_string(ctxt);
+    arg1 = xpath_pop_string(ctxt);
+
+    xpath_push_value(ctxt, xpath_new_boolean(ctxt, wcsicmp(arg1, arg2) != 0));
+
+    free(arg1);
+    free(arg2);
+}
+
+static void xpath_builtin_ilt(struct xpath_parser_context *ctxt, int nargs)
+{
+    WCHAR *arg1, *arg2;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 2))
+        return;
+
+    arg2 = xpath_pop_string(ctxt);
+    arg1 = xpath_pop_string(ctxt);
+
+    xpath_push_value(ctxt, xpath_new_boolean(ctxt, wcsicmp(arg1, arg2) < 0));
+
+    free(arg1);
+    free(arg2);
+}
+
+static void xpath_builtin_ileq(struct xpath_parser_context *ctxt, int nargs)
+{
+    WCHAR *arg1, *arg2;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 2))
+        return;
+
+    arg2 = xpath_pop_string(ctxt);
+    arg1 = xpath_pop_string(ctxt);
+
+    xpath_push_value(ctxt, xpath_new_boolean(ctxt, wcsicmp(arg1, arg2) <= 0));
+
+    free(arg1);
+    free(arg2);
+}
+
+static void xpath_builtin_igt(struct xpath_parser_context *ctxt, int nargs)
+{
+    WCHAR *arg1, *arg2;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 2))
+        return;
+
+    arg2 = xpath_pop_string(ctxt);
+    arg1 = xpath_pop_string(ctxt);
+
+    xpath_push_value(ctxt, xpath_new_boolean(ctxt, wcsicmp(arg1, arg2) > 0));
+
+    free(arg1);
+    free(arg2);
+}
+
+static void xpath_builtin_igeq(struct xpath_parser_context *ctxt, int nargs)
+{
+    WCHAR *arg1, *arg2;
+
+    if (!xpath_builtin_check_stack(ctxt, nargs, 2))
+        return;
+
+    arg2 = xpath_pop_string(ctxt);
+    arg1 = xpath_pop_string(ctxt);
+
+    xpath_push_value(ctxt, xpath_new_boolean(ctxt, wcsicmp(arg1, arg2) >= 0));
+
+    free(arg1);
+    free(arg2);
+}
+
+static void xpath_register_all_functions(struct xpath_context *ctxt)
+{
+    xpath_register_func(ctxt, L"boolean", xpath_builtin_boolean);
+    xpath_register_func(ctxt, L"ceiling", xpath_builtin_ceiling);
+    xpath_register_func(ctxt, L"count", xpath_builtin_count);
+    xpath_register_func(ctxt, L"concat", xpath_builtin_concat);
+    xpath_register_func(ctxt, L"contains", xpath_builtin_contains);
+    xpath_register_func(ctxt, L"false", xpath_builtin_false);
+    xpath_register_func(ctxt, L"floor", xpath_builtin_floor);
+    xpath_register_func(ctxt, L"id", xpath_builtin_id);
+    xpath_register_func(ctxt, L"last", xpath_builtin_last);
+    xpath_register_func(ctxt, L"lang", xpath_builtin_lang);
+    xpath_register_func(ctxt, L"local-name", xpath_builtin_local_name);
+    xpath_register_func(ctxt, L"not", xpath_builtin_not);
+    xpath_register_func(ctxt, L"name", xpath_builtin_name);
+    xpath_register_func(ctxt, L"namespace-uri", xpath_builtin_namespace_uri);
+    xpath_register_func(ctxt, L"normalize-space", xpath_builtin_normalize_space);
+    xpath_register_func(ctxt, L"number", xpath_builtin_number);
+    xpath_register_func(ctxt, L"position", xpath_builtin_position);
+    xpath_register_func(ctxt, L"round", xpath_builtin_round);
+    xpath_register_func(ctxt, L"string", xpath_builtin_string);
+    xpath_register_func(ctxt, L"string-length", xpath_builtin_string_length);
+    xpath_register_func(ctxt, L"starts-with", xpath_builtin_startswith);
+    xpath_register_func(ctxt, L"substring", xpath_builtin_substring);
+    xpath_register_func(ctxt, L"substring-before", xpath_builtin_substringbefore);
+    xpath_register_func(ctxt, L"substring-after", xpath_builtin_substringafter);
+    xpath_register_func(ctxt, L"sum", xpath_builtin_sum);
+    xpath_register_func(ctxt, L"true", xpath_builtin_true);
+    xpath_register_func(ctxt, L"translate", xpath_builtin_translate);
+}
+
+static void xpath_register_xslpattern_functions(struct xpath_context *ctxt)
+{
+    xpath_register_func(ctxt, L"index", xpath_builtin_index);
+    xpath_register_func(ctxt, L"end", xpath_builtin_end);
+    xpath_register_func(ctxt, L"nodeType", xpath_builtin_node_type);
+    xpath_register_func(ctxt, L"OP_IEq", xpath_builtin_ieq);
+    xpath_register_func(ctxt, L"OP_INEq", xpath_builtin_ineq);
+    xpath_register_func(ctxt, L"OP_ILt", xpath_builtin_ilt);
+    xpath_register_func(ctxt, L"OP_ILEq", xpath_builtin_ileq);
+    xpath_register_func(ctxt, L"OP_IGt", xpath_builtin_igt);
+    xpath_register_func(ctxt, L"OP_IGEq", xpath_builtin_igeq);
+}
+
+struct xpath_context * xpath_create_context(bool xpath, struct domnode *node)
+{
+    struct domnode *doc = node->owner ? node->owner : node;
+    struct xpath_context *ret;
+
+    if (!(ret = calloc(1, sizeof(*ret))))
+        return NULL;
+
+    ret->root = node;
+    while (xpath_get_parent(ret->root))
+        ret->root = xpath_get_parent(ret->root);
+    ret->node = node;
+    ret->properties = doc->properties;
+
+    ret->xpath = xpath;
+    ret->contextSize = -1;
+    ret->proximityPosition = -1;
+
+    xpath_register_all_functions(ret);
+    if (!xpath)
+        xpath_register_xslpattern_functions(ret);
+
+    return ret;
+}
+
+void xpath_free_context(struct xpath_context *ctxt)
+{
+    for (size_t i = 0; i < ctxt->functions.count; ++i)
+    {
+        free(ctxt->functions.entries[i].name);
+        free(ctxt->functions.entries[i].uri);
+    }
+    free(ctxt->functions.entries);
+    free(ctxt);
+}
+
+HRESULT xpath_parse_selection_namespaces(const WCHAR *value, struct domdoc_properties *properties)
+{
+    struct xpath_parser_context ctxt = { 0 };
+    struct xpath_namespace *ns;
+    struct xpath_qname name;
+    HRESULT hr = S_OK;
+    WCHAR *uri;
+
+    ctxt.cur = ctxt.base = value;
+
+    while (*ctxt.cur)
+    {
+        xpath_parse_skipspaces(&ctxt);
+        xpath_parse_qname(&ctxt, &name);
+        if (!name.name)
+        {
+            xpath_free_qname(&name);
+            hr = E_FAIL;
+            break;
+        }
+
+        if ((name.prefix && wcscmp(name.prefix, L"xmlns"))
+                || (!name.prefix && wcscmp(name.name, L"xmlns")))
+        {
+            xpath_free_qname(&name);
+            hr = E_FAIL;
+            break;
+        }
+
+        xpath_parse_skipspaces(&ctxt);
+        if (*ctxt.cur != '=')
+        {
+            xpath_free_qname(&name);
+            hr = E_FAIL;
+            break;
+        }
+        xpath_parse_next(&ctxt);
+        xpath_parse_skipspaces(&ctxt);
+        if (!(uri = xpath_parse_literal(&ctxt)))
+        {
+            xpath_free_qname(&name);
+            hr = E_FAIL;
+            break;
+        }
+        xpath_parse_skipspaces(&ctxt);
+
+        ns = xpath_malloc(&ctxt, sizeof(*ns));
+        ns->prefix = name.name;
+        ns->uri = uri;
+
+        free(name.prefix);
+
+        list_add_tail(&properties->namespaces.entries, &ns->entry);
+    }
+
+    if (FAILED(hr))
+        domdoc_properties_clear_selection_namespaces(properties);
+
+    return hr;
+}
diff --git a/dlls/msxml3/xpath.h b/dlls/msxml3/xpath.h
new file mode 100644
index 000000000000..15840fbdc659
--- /dev/null
+++ b/dlls/msxml3/xpath.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2026 Nikolay Sivov 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 "msxml_private.h"
+
+struct xpath_nodeset
+{
+    size_t count;
+    size_t capacity;
+    struct domnode **nodes;
+};
+
+enum xpath_object_type
+{
+    _XPATH_UNDEFINED = 0,
+    _XPATH_NODESET = 1,
+    _XPATH_BOOLEAN = 2,
+    _XPATH_NUMBER = 3,
+    _XPATH_STRING = 4,
+};
+
+struct xpath_object
+{
+    enum xpath_object_type type;
+    struct xpath_nodeset *nodesetval;
+    int boolval;
+    double floatval;
+    WCHAR *stringval;
+    void *user;
+    int index;
+    void *user2;
+    int index2;
+};
+
+struct xpath_namespace
+{
+    struct list entry;
+    WCHAR *prefix;
+    WCHAR *uri;
+};
+
+struct xpath_context;
+
+extern struct xpath_context * xpath_create_context(bool xpath, struct domnode *node);
+extern void xpath_free_context(struct xpath_context *ctxt);
+extern HRESULT xpath_parse_selection_namespaces(const WCHAR *value, struct domdoc_properties *properties);
+extern struct xpath_object * xpath_eval(const WCHAR *str, struct xpath_context *ctx);
+extern void xpath_free_object(struct xpath_object *obj);
+extern WCHAR *xpath_translate_function(const WCHAR *to, const WCHAR *from, const WCHAR *str);
diff --git a/dlls/ndfapi/Makefile.in b/dlls/ndfapi/Makefile.in
new file mode 100644
index 000000000000..2b2d0ed2844c
--- /dev/null
+++ b/dlls/ndfapi/Makefile.in
@@ -0,0 +1,4 @@
+MODULE    = ndfapi.dll
+
+SOURCES = \
+	ndfapi_main.c
diff --git a/dlls/ndfapi/ndfapi.spec b/dlls/ndfapi/ndfapi.spec
new file mode 100755
index 000000000000..df2d78dca450
--- /dev/null
+++ b/dlls/ndfapi/ndfapi.spec
@@ -0,0 +1,28 @@
+@ stdcall -private DllCanUnloadNow()
+@ stub -private DllGetClassObject(ptr ptr ptr)
+@ stdcall -private DllRegisterServer()
+@ stdcall -private DllUnregisterServer()
+@ stub NdfRunDllDiagnoseIncident
+@ stub NdfRunDllDiagnoseNetConnectionIncident
+@ stub NdfRunDllDiagnoseWithAnswerFile
+@ stub NdfRunDllDuplicateIPDefendingSystem
+@ stub NdfRunDllDuplicateIPOffendingSystem
+@ stub NdfRunDllHelpTopic
+@ stub NdfCancelIncident
+@ stdcall NdfCloseIncident(ptr)
+@ stdcall NdfCreateConnectivityIncident(ptr)
+@ stub NdfCreateDNSIncident
+@ stub NdfCreateGroupingIncident
+@ stub NdfCreateInboundIncident
+@ stub NdfCreateIncident
+@ stub NdfCreateNetConnectionIncident
+@ stub NdfCreatePnrpIncident
+@ stub NdfCreateSharingIncident
+@ stdcall NdfCreateWebIncident(str ptr)
+@ stub NdfCreateWebIncidentEx
+@ stdcall NdfCreateWinSockIncident(ptr str long str ptr ptr)
+@ stdcall NdfDiagnoseIncident(ptr ptr ptr long long)
+@ stdcall NdfExecuteDiagnosis(ptr ptr)
+@ stub NdfGetTraceFile
+@ stub NdfRepairIncident
+@ stub NdfRepairIncidentEx
diff --git a/dlls/ndfapi/ndfapi_main.c b/dlls/ndfapi/ndfapi_main.c
new file mode 100644
index 000000000000..902fdd058c85
--- /dev/null
+++ b/dlls/ndfapi/ndfapi_main.c
@@ -0,0 +1,70 @@
+/*
+* Copyright 2026 Alistair Leslie-Hughes
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winsock2.h"
+
+#include "ndfapi.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(ndfapi);
+
+STDAPI NdfCloseIncident(NDFHANDLE handle)
+{
+    FIXME("%p\n", handle);
+
+    return E_NOTIMPL;
+}
+
+STDAPI NdfCreateConnectivityIncident(NDFHANDLE *handle)
+{
+    FIXME("%p\n", handle);
+
+    return E_NOTIMPL;
+}
+
+STDAPI NdfCreateWebIncident(LPCWSTR url, NDFHANDLE *handle)
+{
+    FIXME("%s, %p\n", debugstr_w(url), handle);
+
+    return E_NOTIMPL;
+}
+
+STDAPI NdfCreateWinSockIncident(SOCKET sock, LPCWSTR host, USHORT port, LPCWSTR appId, SID *userId, NDFHANDLE *handle)
+{
+    FIXME("%Ix, %s, %s, %p, %p\n", sock, debugstr_w(host), debugstr_w(appId), userId, handle);
+
+    return E_NOTIMPL;
+}
+
+STDAPI NdfDiagnoseIncident(NDFHANDLE handle, ULONG *count, RootCauseInfo **causes, DWORD wait, DWORD flags)
+{
+    FIXME("%p, %p, %p, %ld, %ld\n", handle, count, causes, wait, flags);
+
+    return E_NOTIMPL;
+}
+
+STDAPI NdfExecuteDiagnosis(NDFHANDLE handle, HWND hwnd)
+{
+    FIXME("%p, %p\n", handle, hwnd);
+
+    return E_NOTIMPL;
+}
diff --git a/dlls/netapi32/Makefile.in b/dlls/netapi32/Makefile.in
index 1ee1f8bfd8f3..2f372972a212 100644
--- a/dlls/netapi32/Makefile.in
+++ b/dlls/netapi32/Makefile.in
@@ -1,7 +1,7 @@
 MODULE    = netapi32.dll
 UNIXLIB   = netapi32.so
 IMPORTLIB = netapi32
-IMPORTS   = rpcrt4 iphlpapi ws2_32 advapi32 dnsapi netutils
+IMPORTS   = rpcrt4 iphlpapi ws2_32 advapi32 dnsapi netutils wkscli
 
 SOURCES = \
 	atsvc.idl \
diff --git a/dlls/netapi32/netapi32.c b/dlls/netapi32/netapi32.c
index 659cee09d7da..9737f2ae3703 100644
--- a/dlls/netapi32/netapi32.c
+++ b/dlls/netapi32/netapi32.c
@@ -294,14 +294,6 @@ NET_API_STATUS WINAPI NetStatisticsGet(LMSTR server, LMSTR service,
     return res;
 }
 
-NET_API_STATUS WINAPI NetUseEnum(LMSTR server, DWORD level, LPBYTE* bufptr, DWORD prefmaxsize,
-                          LPDWORD entriesread, LPDWORD totalentries, LPDWORD resumehandle)
-{
-    FIXME("stub (%p, %ld, %p, %ld, %p, %p, %p)\n", server, level, bufptr, prefmaxsize,
-           entriesread, totalentries, resumehandle);
-    return ERROR_NOT_SUPPORTED;
-}
-
 NET_API_STATUS WINAPI NetScheduleJobAdd(LPCWSTR server, LPBYTE bufptr, LPDWORD jobid)
 {
     TRACE("(%s, %p, %p)\n", debugstr_w(server), bufptr, jobid);
@@ -339,13 +331,6 @@ NET_API_STATUS WINAPI NetScheduleJobGetInfo(LPCWSTR server, DWORD jobid, LPBYTE
     return NetrJobGetInfo(server, jobid, (LPAT_INFO *)bufptr);
 }
 
-NET_API_STATUS WINAPI NetUseGetInfo(LMSTR server, LMSTR name, DWORD level, LPBYTE *bufptr)
-{
-    FIXME("stub (%p, %p, %ld, %p)\n", server, name, level, bufptr);
-    return ERROR_NOT_SUPPORTED;
-
-}
-
 /************************************************************
  * NetSessionEnum  (NETAPI32.@)
  *
@@ -1022,28 +1007,6 @@ NET_API_STATUS WINAPI NetWkstaGetInfo( LMSTR servername, DWORD level,
     return ret;
 }
 
-/************************************************************
- *                NetGetJoinInformation (NETAPI32.@)
- */
-NET_API_STATUS NET_API_FUNCTION NetGetJoinInformation(
-    LPCWSTR Server,
-    LPWSTR *Name,
-    PNETSETUP_JOIN_STATUS type)
-{
-    static const WCHAR workgroupW[] = L"Workgroup";
-
-    FIXME("Semi-stub %s %p %p\n", wine_dbgstr_w(Server), Name, type);
-
-    if (!Name || !type)
-        return ERROR_INVALID_PARAMETER;
-
-    NetApiBufferAllocate(sizeof(workgroupW), (LPVOID *)Name);
-    lstrcpyW(*Name, workgroupW);
-    *type = NetSetupWorkgroupName;
-
-    return NERR_Success;
-}
-
 /************************************************************
  *                NetUserGetGroups (NETAPI32.@)
  */
@@ -2040,18 +2003,6 @@ NET_API_STATUS WINAPI NetUserChangePassword(LPCWSTR domainname, LPCWSTR username
     return NERR_Success;
 }
 
-NET_API_STATUS WINAPI NetUseAdd(LMSTR servername, DWORD level, LPBYTE bufptr, LPDWORD parm_err)
-{
-    FIXME("%s %ld %p %p stub\n", debugstr_w(servername), level, bufptr, parm_err);
-    return NERR_Success;
-}
-
-NET_API_STATUS WINAPI NetUseDel(LMSTR servername, LMSTR usename, DWORD forcecond)
-{
-    FIXME("%s %s %ld stub\n", debugstr_w(servername), debugstr_w(usename), forcecond);
-    return NERR_Success;
-}
-
 /************************************************************
  *                I_BrowserSetNetlogonState  (NETAPI32.@)
  */
diff --git a/dlls/netapi32/netapi32.spec b/dlls/netapi32/netapi32.spec
index ece7ceec6bdb..4906bfade6d4 100644
--- a/dlls/netapi32/netapi32.spec
+++ b/dlls/netapi32/netapi32.spec
@@ -99,7 +99,7 @@
 @ stdcall NetGetAnyDCName(wstr wstr ptr)
 @ stdcall NetGetDCName(wstr wstr ptr)
 @ stub NetGetDisplayInformationIndex
-@ stdcall NetGetJoinInformation(wstr ptr ptr)
+@ stdcall NetGetJoinInformation(wstr ptr ptr) wkscli.NetGetJoinInformation
 @ stub NetGroupAdd
 @ stdcall NetGroupAddUser(wstr wstr wstr)
 @ stub NetGroupDel
@@ -203,10 +203,10 @@
 @ stdcall NetShareGetInfo(wstr wstr long ptr)
 @ stub NetShareSetInfo
 @ stdcall NetStatisticsGet(wstr wstr long long ptr)
-@ stdcall NetUseAdd(wstr long ptr ptr)
-@ stdcall NetUseDel(wstr wstr long)
-@ stdcall NetUseEnum(wstr long ptr long ptr ptr ptr)
-@ stdcall NetUseGetInfo(ptr ptr long ptr)
+@ stdcall NetUseAdd(wstr long ptr ptr) wkscli.NetUseAdd
+@ stdcall NetUseDel(wstr wstr long) wkscli.NetUseDel
+@ stdcall NetUseEnum(wstr long ptr long ptr ptr ptr) wkscli.NetUseEnum
+@ stdcall NetUseGetInfo(ptr ptr long ptr) wkscli.NetUseGetInfo
 @ stdcall NetUserAdd(wstr long ptr ptr)
 @ stdcall NetUserChangePassword(wstr wstr wstr wstr)
 @ stdcall NetUserDel(wstr wstr)
diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in
index 022583aa20e2..460aada5c5ed 100644
--- a/dlls/ntdll/Makefile.in
+++ b/dlls/ntdll/Makefile.in
@@ -49,6 +49,7 @@ SOURCES = \
 	thread.c \
 	threadpool.c \
 	time.c \
+	unix/alpc.c \
 	unix/cdrom.c \
 	unix/debug.c \
 	unix/env.c \
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index f396c334e2d1..0b6d9b2cbf45 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -155,6 +155,12 @@
 @ stdcall -syscall NtAllocateUuids(ptr ptr ptr ptr)
 @ stdcall -syscall=0x0018 NtAllocateVirtualMemory(long ptr long ptr long long)
 @ stdcall -syscall NtAllocateVirtualMemoryEx(long ptr ptr long long ptr long)
+@ stdcall -syscall NtAlpcAcceptConnectPort(ptr ptr long ptr ptr ptr ptr ptr long)
+@ stdcall -syscall NtAlpcConnectPort(ptr ptr ptr ptr long ptr ptr ptr ptr ptr ptr)
+@ stdcall -syscall NtAlpcCreatePort(ptr ptr ptr)
+@ stdcall -syscall NtAlpcDisconnectPort(ptr long)
+@ stdcall -syscall NtAlpcImpersonateClientOfPort(ptr ptr ptr)
+@ stdcall -syscall NtAlpcSendWaitReceivePort(ptr long ptr ptr ptr ptr ptr ptr)
 @ stub -syscall=0x004c NtApphelpCacheControl
 @ stdcall -syscall NtAreMappedFilesTheSame(ptr ptr)
 @ stdcall -syscall NtAssignProcessToJobObject(long long)
@@ -1229,6 +1235,12 @@
 @ stdcall -private ZwAllocateUuids(ptr ptr ptr ptr) NtAllocateUuids
 @ stdcall -private ZwAllocateVirtualMemory(long ptr long ptr long long) NtAllocateVirtualMemory
 @ stdcall -private ZwAllocateVirtualMemoryEx(long ptr ptr long long ptr long) NtAllocateVirtualMemoryEx
+@ stdcall -private ZwAlpcAcceptConnectPort(ptr ptr long ptr ptr ptr ptr ptr long) NtAlpcAcceptConnectPort
+@ stdcall -private ZwAlpcConnectPort(ptr ptr ptr ptr long ptr ptr ptr ptr ptr ptr) NtAlpcConnectPort
+@ stdcall -private ZwAlpcCreatePort(ptr ptr ptr) NtAlpcCreatePort
+@ stdcall -private ZwAlpcDisconnectPort(ptr long) NtAlpcDisconnectPort
+@ stdcall -private ZwAlpcImpersonateClientOfPort(ptr ptr ptr) NtAlpcImpersonateClientOfPort
+@ stdcall -private ZwAlpcSendWaitReceivePort(ptr long ptr ptr ptr ptr ptr ptr) NtAlpcSendWaitReceivePort
 @ stdcall -private ZwApphelpCacheControl() NtApphelpCacheControl
 @ stdcall -private ZwAreMappedFilesTheSame(ptr ptr) NtAreMappedFilesTheSame
 @ stdcall -private ZwAssignProcessToJobObject(long long) NtAssignProcessToJobObject
diff --git a/dlls/ntdll/ntsyscalls.h b/dlls/ntdll/ntsyscalls.h
index ba7cc060c66c..05b121f25800 100644
--- a/dlls/ntdll/ntsyscalls.h
+++ b/dlls/ntdll/ntsyscalls.h
@@ -109,162 +109,168 @@
     SYSCALL_ENTRY( 0x0069, NtAllocateReserveObject, 12 ) \
     SYSCALL_ENTRY( 0x006a, NtAllocateUuids, 16 ) \
     SYSCALL_ENTRY( 0x006b, NtAllocateVirtualMemoryEx, 28 ) \
-    SYSCALL_ENTRY( 0x006c, NtAreMappedFilesTheSame, 8 ) \
-    SYSCALL_ENTRY( 0x006d, NtAssignProcessToJobObject, 8 ) \
-    SYSCALL_ENTRY( 0x006e, NtCancelIoFileEx, 12 ) \
-    SYSCALL_ENTRY( 0x006f, NtCancelSynchronousIoFile, 12 ) \
-    SYSCALL_ENTRY( 0x0070, NtCommitTransaction, 8 ) \
-    SYSCALL_ENTRY( 0x0071, NtCompareObjects, 8 ) \
-    SYSCALL_ENTRY( 0x0072, NtCompareTokens, 12 ) \
-    SYSCALL_ENTRY( 0x0073, NtCompleteConnectPort, 4 ) \
-    SYSCALL_ENTRY( 0x0074, NtConnectPort, 32 ) \
-    SYSCALL_ENTRY( 0x0075, NtContinueEx, 8 ) \
-    SYSCALL_ENTRY( 0x0076, NtConvertBetweenAuxiliaryCounterAndPerformanceCounter, 16 ) \
-    SYSCALL_ENTRY( 0x0077, NtCreateDirectoryObject, 12 ) \
-    SYSCALL_ENTRY( 0x0078, NtCreateIoCompletion, 16 ) \
-    SYSCALL_ENTRY( 0x0079, NtCreateJobObject, 12 ) \
-    SYSCALL_ENTRY( 0x007a, NtCreateKeyTransacted, 32 ) \
-    SYSCALL_ENTRY( 0x007b, NtCreateKeyedEvent, 16 ) \
-    SYSCALL_ENTRY( 0x007c, NtCreateLowBoxToken, 36 ) \
-    SYSCALL_ENTRY( 0x007d, NtCreateMailslotFile, 32 ) \
-    SYSCALL_ENTRY( 0x007e, NtCreateMutant, 16 ) \
-    SYSCALL_ENTRY( 0x007f, NtCreateNamedPipeFile, 56 ) \
-    SYSCALL_ENTRY( 0x0080, NtCreatePagingFile, 16 ) \
-    SYSCALL_ENTRY( 0x0081, NtCreatePort, 20 ) \
-    SYSCALL_ENTRY( 0x0082, NtCreateSectionEx, 36 ) \
-    SYSCALL_ENTRY( 0x0083, NtCreateSemaphore, 20 ) \
-    SYSCALL_ENTRY( 0x0084, NtCreateSymbolicLinkObject, 16 ) \
-    SYSCALL_ENTRY( 0x0085, NtCreateThreadEx, 44 ) \
-    SYSCALL_ENTRY( 0x0086, NtCreateTimer, 16 ) \
-    SYSCALL_ENTRY( 0x0087, NtCreateToken, 52 ) \
-    SYSCALL_ENTRY( 0x0088, NtCreateTransaction, 40 ) \
-    SYSCALL_ENTRY( 0x0089, NtCreateUserProcess, 44 ) \
-    SYSCALL_ENTRY( 0x008a, NtDebugActiveProcess, 8 ) \
-    SYSCALL_ENTRY( 0x008b, NtDebugContinue, 12 ) \
-    SYSCALL_ENTRY( 0x008c, NtDeleteAtom, 4 ) \
-    SYSCALL_ENTRY( 0x008d, NtDeleteFile, 4 ) \
-    SYSCALL_ENTRY( 0x008e, NtDeleteKey, 4 ) \
-    SYSCALL_ENTRY( 0x008f, NtDeleteValueKey, 8 ) \
-    SYSCALL_ENTRY( 0x0090, NtDisplayString, 4 ) \
-    SYSCALL_ENTRY( 0x0091, NtFilterToken, 24 ) \
-    SYSCALL_ENTRY( 0x0092, NtFlushBuffersFileEx, 20 ) \
-    SYSCALL_ENTRY( 0x0093, NtFlushInstructionCache, 12 ) \
-    SYSCALL_ENTRY( 0x0094, NtFlushKey, 4 ) \
-    SYSCALL_ENTRY( 0x0095, NtFlushProcessWriteBuffers, 0 ) \
-    SYSCALL_ENTRY( 0x0096, NtFlushVirtualMemory, 16 ) \
-    SYSCALL_ENTRY( 0x0097, NtGetContextThread, 8 ) \
-    SYSCALL_ENTRY( 0x0098, NtGetCurrentProcessorNumber, 0 ) \
-    SYSCALL_ENTRY( 0x0099, NtGetNextProcess, 20 ) \
-    SYSCALL_ENTRY( 0x009a, NtGetNextThread, 24 ) \
-    SYSCALL_ENTRY( 0x009b, NtGetNlsSectionPtr, 20 ) \
-    SYSCALL_ENTRY( 0x009c, NtGetWriteWatch, 28 ) \
-    SYSCALL_ENTRY( 0x009d, NtImpersonateAnonymousToken, 4 ) \
-    SYSCALL_ENTRY( 0x009e, NtInitializeNlsFiles, 12 ) \
-    SYSCALL_ENTRY( 0x009f, NtInitiatePowerAction, 16 ) \
-    SYSCALL_ENTRY( 0x00a0, NtListenPort, 8 ) \
-    SYSCALL_ENTRY( 0x00a1, NtLoadDriver, 4 ) \
-    SYSCALL_ENTRY( 0x00a2, NtLoadKey, 8 ) \
-    SYSCALL_ENTRY( 0x00a3, NtLoadKey2, 12 ) \
-    SYSCALL_ENTRY( 0x00a4, NtLoadKeyEx, 32 ) \
-    SYSCALL_ENTRY( 0x00a5, NtLockFile, 40 ) \
+    SYSCALL_ENTRY( 0x006c, NtAlpcAcceptConnectPort, 36 ) \
+    SYSCALL_ENTRY( 0x006d, NtAlpcConnectPort, 44 ) \
+    SYSCALL_ENTRY( 0x006e, NtAlpcCreatePort, 12 ) \
+    SYSCALL_ENTRY( 0x006f, NtAlpcDisconnectPort, 8 ) \
+    SYSCALL_ENTRY( 0x0070, NtAlpcImpersonateClientOfPort, 12 ) \
+    SYSCALL_ENTRY( 0x0071, NtAlpcSendWaitReceivePort, 32 ) \
+    SYSCALL_ENTRY( 0x0072, NtAreMappedFilesTheSame, 8 ) \
+    SYSCALL_ENTRY( 0x0073, NtAssignProcessToJobObject, 8 ) \
+    SYSCALL_ENTRY( 0x0074, NtCancelIoFileEx, 12 ) \
+    SYSCALL_ENTRY( 0x0075, NtCancelSynchronousIoFile, 12 ) \
+    SYSCALL_ENTRY( 0x0076, NtCommitTransaction, 8 ) \
+    SYSCALL_ENTRY( 0x0077, NtCompareObjects, 8 ) \
+    SYSCALL_ENTRY( 0x0078, NtCompareTokens, 12 ) \
+    SYSCALL_ENTRY( 0x0079, NtCompleteConnectPort, 4 ) \
+    SYSCALL_ENTRY( 0x007a, NtConnectPort, 32 ) \
+    SYSCALL_ENTRY( 0x007b, NtContinueEx, 8 ) \
+    SYSCALL_ENTRY( 0x007c, NtConvertBetweenAuxiliaryCounterAndPerformanceCounter, 16 ) \
+    SYSCALL_ENTRY( 0x007d, NtCreateDirectoryObject, 12 ) \
+    SYSCALL_ENTRY( 0x007e, NtCreateIoCompletion, 16 ) \
+    SYSCALL_ENTRY( 0x007f, NtCreateJobObject, 12 ) \
+    SYSCALL_ENTRY( 0x0080, NtCreateKeyTransacted, 32 ) \
+    SYSCALL_ENTRY( 0x0081, NtCreateKeyedEvent, 16 ) \
+    SYSCALL_ENTRY( 0x0082, NtCreateLowBoxToken, 36 ) \
+    SYSCALL_ENTRY( 0x0083, NtCreateMailslotFile, 32 ) \
+    SYSCALL_ENTRY( 0x0084, NtCreateMutant, 16 ) \
+    SYSCALL_ENTRY( 0x0085, NtCreateNamedPipeFile, 56 ) \
+    SYSCALL_ENTRY( 0x0086, NtCreatePagingFile, 16 ) \
+    SYSCALL_ENTRY( 0x0087, NtCreatePort, 20 ) \
+    SYSCALL_ENTRY( 0x0088, NtCreateSectionEx, 36 ) \
+    SYSCALL_ENTRY( 0x0089, NtCreateSemaphore, 20 ) \
+    SYSCALL_ENTRY( 0x008a, NtCreateSymbolicLinkObject, 16 ) \
+    SYSCALL_ENTRY( 0x008b, NtCreateThreadEx, 44 ) \
+    SYSCALL_ENTRY( 0x008c, NtCreateTimer, 16 ) \
+    SYSCALL_ENTRY( 0x008d, NtCreateToken, 52 ) \
+    SYSCALL_ENTRY( 0x008e, NtCreateTransaction, 40 ) \
+    SYSCALL_ENTRY( 0x008f, NtCreateUserProcess, 44 ) \
+    SYSCALL_ENTRY( 0x0090, NtDebugActiveProcess, 8 ) \
+    SYSCALL_ENTRY( 0x0091, NtDebugContinue, 12 ) \
+    SYSCALL_ENTRY( 0x0092, NtDeleteAtom, 4 ) \
+    SYSCALL_ENTRY( 0x0093, NtDeleteFile, 4 ) \
+    SYSCALL_ENTRY( 0x0094, NtDeleteKey, 4 ) \
+    SYSCALL_ENTRY( 0x0095, NtDeleteValueKey, 8 ) \
+    SYSCALL_ENTRY( 0x0096, NtDisplayString, 4 ) \
+    SYSCALL_ENTRY( 0x0097, NtFilterToken, 24 ) \
+    SYSCALL_ENTRY( 0x0098, NtFlushBuffersFileEx, 20 ) \
+    SYSCALL_ENTRY( 0x0099, NtFlushInstructionCache, 12 ) \
+    SYSCALL_ENTRY( 0x009a, NtFlushKey, 4 ) \
+    SYSCALL_ENTRY( 0x009b, NtFlushProcessWriteBuffers, 0 ) \
+    SYSCALL_ENTRY( 0x009c, NtFlushVirtualMemory, 16 ) \
+    SYSCALL_ENTRY( 0x009d, NtGetContextThread, 8 ) \
+    SYSCALL_ENTRY( 0x009e, NtGetCurrentProcessorNumber, 0 ) \
+    SYSCALL_ENTRY( 0x009f, NtGetNextProcess, 20 ) \
+    SYSCALL_ENTRY( 0x00a0, NtGetNextThread, 24 ) \
+    SYSCALL_ENTRY( 0x00a1, NtGetNlsSectionPtr, 20 ) \
+    SYSCALL_ENTRY( 0x00a2, NtGetWriteWatch, 28 ) \
+    SYSCALL_ENTRY( 0x00a3, NtImpersonateAnonymousToken, 4 ) \
+    SYSCALL_ENTRY( 0x00a4, NtInitializeNlsFiles, 12 ) \
+    SYSCALL_ENTRY( 0x00a5, NtInitiatePowerAction, 16 ) \
     SYSCALL_ENTRY( 0x00a6, NtCreateDebugObject, 16 ) \
-    SYSCALL_ENTRY( 0x00a7, NtLockVirtualMemory, 16 ) \
-    SYSCALL_ENTRY( 0x00a8, NtMakePermanentObject, 4 ) \
-    SYSCALL_ENTRY( 0x00a9, NtMakeTemporaryObject, 4 ) \
-    SYSCALL_ENTRY( 0x00aa, NtMapViewOfSectionEx, 36 ) \
-    SYSCALL_ENTRY( 0x00ab, NtNotifyChangeDirectoryFile, 36 ) \
-    SYSCALL_ENTRY( 0x00ac, NtNotifyChangeKey, 40 ) \
-    SYSCALL_ENTRY( 0x00ad, NtNotifyChangeMultipleKeys, 48 ) \
-    SYSCALL_ENTRY( 0x00ae, NtOpenIoCompletion, 12 ) \
-    SYSCALL_ENTRY( 0x00af, NtOpenJobObject, 12 ) \
-    SYSCALL_ENTRY( 0x00b0, NtOpenKeyEx, 16 ) \
-    SYSCALL_ENTRY( 0x00b1, NtOpenKeyTransacted, 16 ) \
-    SYSCALL_ENTRY( 0x00b2, NtOpenKeyTransactedEx, 20 ) \
-    SYSCALL_ENTRY( 0x00b3, NtOpenKeyedEvent, 12 ) \
-    SYSCALL_ENTRY( 0x00b4, NtOpenMutant, 12 ) \
-    SYSCALL_ENTRY( 0x00b5, NtOpenProcessToken, 12 ) \
-    SYSCALL_ENTRY( 0x00b6, NtOpenSemaphore, 12 ) \
-    SYSCALL_ENTRY( 0x00b7, NtOpenSymbolicLinkObject, 12 ) \
-    SYSCALL_ENTRY( 0x00b8, NtOpenThread, 16 ) \
-    SYSCALL_ENTRY( 0x00b9, NtOpenTimer, 12 ) \
-    SYSCALL_ENTRY( 0x00ba, NtPrivilegeCheck, 12 ) \
-    SYSCALL_ENTRY( 0x00bb, NtPulseEvent, 8 ) \
-    SYSCALL_ENTRY( 0x00bc, NtQueryDirectoryObject, 28 ) \
-    SYSCALL_ENTRY( 0x00bd, NtQueryEaFile, 36 ) \
-    SYSCALL_ENTRY( 0x00be, NtQueryFullAttributesFile, 8 ) \
-    SYSCALL_ENTRY( 0x00bf, NtQueryInformationAtom, 20 ) \
-    SYSCALL_ENTRY( 0x00c0, NtQueryInformationJobObject, 20 ) \
-    SYSCALL_ENTRY( 0x00c1, NtQueryInstallUILanguage, 4 ) \
-    SYSCALL_ENTRY( 0x00c2, NtQueryIoCompletion, 20 ) \
-    SYSCALL_ENTRY( 0x00c3, NtQueryLicenseValue, 20 ) \
-    SYSCALL_ENTRY( 0x00c4, NtQueryMultipleValueKey, 24 ) \
-    SYSCALL_ENTRY( 0x00c5, NtQueryMutant, 20 ) \
-    SYSCALL_ENTRY( 0x00c6, NtQuerySecurityObject, 20 ) \
-    SYSCALL_ENTRY( 0x00c7, NtQuerySemaphore, 20 ) \
-    SYSCALL_ENTRY( 0x00c8, NtQuerySymbolicLinkObject, 12 ) \
-    SYSCALL_ENTRY( 0x00c9, NtQuerySystemEnvironmentValue, 16 ) \
-    SYSCALL_ENTRY( 0x00ca, NtQuerySystemEnvironmentValueEx, 20 ) \
-    SYSCALL_ENTRY( 0x00cb, NtQuerySystemInformationEx, 24 ) \
-    SYSCALL_ENTRY( 0x00cc, NtQueryTimerResolution, 12 ) \
-    SYSCALL_ENTRY( 0x00cd, NtQueueApcThreadEx, 24 ) \
-    SYSCALL_ENTRY( 0x00ce, NtQueueApcThreadEx2, 28 ) \
-    SYSCALL_ENTRY( 0x00cf, NtRaiseException, 12 ) \
-    SYSCALL_ENTRY( 0x00d0, NtRaiseHardError, 24 ) \
-    SYSCALL_ENTRY( 0x00d1, NtRegisterThreadTerminatePort, 4 ) \
-    SYSCALL_ENTRY( 0x00d2, NtReleaseKeyedEvent, 16 ) \
-    SYSCALL_ENTRY( 0x00d3, NtRemoveIoCompletionEx, 24 ) \
-    SYSCALL_ENTRY( 0x00d4, NtRemoveProcessDebug, 8 ) \
-    SYSCALL_ENTRY( 0x00d5, NtRenameKey, 8 ) \
-    SYSCALL_ENTRY( 0x00d6, NtReplaceKey, 12 ) \
-    SYSCALL_ENTRY( 0x00d7, NtResetEvent, 8 ) \
-    SYSCALL_ENTRY( 0x00d8, NtResetWriteWatch, 12 ) \
-    SYSCALL_ENTRY( 0x00d9, NtRestoreKey, 12 ) \
-    SYSCALL_ENTRY( 0x00da, NtResumeProcess, 4 ) \
-    SYSCALL_ENTRY( 0x00db, NtRollbackTransaction, 8 ) \
-    SYSCALL_ENTRY( 0x00dc, NtSaveKey, 8 ) \
-    SYSCALL_ENTRY( 0x00dd, NtSecureConnectPort, 36 ) \
-    SYSCALL_ENTRY( 0x00de, NtSetContextThread, 8 ) \
-    SYSCALL_ENTRY( 0x00df, NtSetDebugFilterState, 12 ) \
-    SYSCALL_ENTRY( 0x00e0, NtSetDefaultLocale, 8 ) \
-    SYSCALL_ENTRY( 0x00e1, NtSetDefaultUILanguage, 4 ) \
-    SYSCALL_ENTRY( 0x00e2, NtSetEaFile, 16 ) \
-    SYSCALL_ENTRY( 0x00e3, NtSetInformationDebugObject, 20 ) \
-    SYSCALL_ENTRY( 0x00e4, NtSetInformationJobObject, 16 ) \
-    SYSCALL_ENTRY( 0x00e5, NtSetInformationKey, 16 ) \
-    SYSCALL_ENTRY( 0x00e6, NtSetInformationToken, 16 ) \
-    SYSCALL_ENTRY( 0x00e7, NtSetInformationVirtualMemory, 24 ) \
-    SYSCALL_ENTRY( 0x00e8, NtSetIntervalProfile, 8 ) \
-    SYSCALL_ENTRY( 0x00e9, NtSetIoCompletion, 20 ) \
-    SYSCALL_ENTRY( 0x00ea, NtSetIoCompletionEx, 24 ) \
-    SYSCALL_ENTRY( 0x00eb, NtSetLdtEntries, 24 ) \
-    SYSCALL_ENTRY( 0x00ec, NtSetSecurityObject, 12 ) \
-    SYSCALL_ENTRY( 0x00ed, NtSetSystemInformation, 12 ) \
-    SYSCALL_ENTRY( 0x00ee, NtSetSystemTime, 8 ) \
-    SYSCALL_ENTRY( 0x00ef, NtSetThreadExecutionState, 8 ) \
-    SYSCALL_ENTRY( 0x00f0, NtSetTimerResolution, 12 ) \
-    SYSCALL_ENTRY( 0x00f1, NtSetVolumeInformationFile, 20 ) \
-    SYSCALL_ENTRY( 0x00f2, NtShutdownSystem, 4 ) \
-    SYSCALL_ENTRY( 0x00f3, NtSignalAndWaitForSingleObject, 16 ) \
-    SYSCALL_ENTRY( 0x00f4, NtSuspendProcess, 4 ) \
-    SYSCALL_ENTRY( 0x00f5, NtSuspendThread, 8 ) \
-    SYSCALL_ENTRY( 0x00f6, NtSystemDebugControl, 24 ) \
-    SYSCALL_ENTRY( 0x00f7, NtTerminateJobObject, 8 ) \
-    SYSCALL_ENTRY( 0x00f8, NtTestAlert, 0 ) \
-    SYSCALL_ENTRY( 0x00f9, NtTraceControl, 24 ) \
-    SYSCALL_ENTRY( 0x00fa, NtUnloadDriver, 4 ) \
-    SYSCALL_ENTRY( 0x00fb, NtUnloadKey, 4 ) \
-    SYSCALL_ENTRY( 0x00fc, NtUnlockFile, 20 ) \
-    SYSCALL_ENTRY( 0x00fd, NtUnlockVirtualMemory, 16 ) \
-    SYSCALL_ENTRY( 0x00fe, NtUnmapViewOfSectionEx, 12 ) \
-    SYSCALL_ENTRY( 0x00ff, NtWaitForAlertByThreadId, 8 ) \
-    SYSCALL_ENTRY( 0x0100, NtWaitForDebugEvent, 16 ) \
-    SYSCALL_ENTRY( 0x0101, NtWaitForKeyedEvent, 16 ) \
-    SYSCALL_ENTRY( 0x0102, NtWow64AllocateVirtualMemory64, 28 ) \
-    SYSCALL_ENTRY( 0x0103, NtWow64GetNativeSystemInformation, 16 ) \
-    SYSCALL_ENTRY( 0x0104, NtWow64IsProcessorFeaturePresent, 4 ) \
-    SYSCALL_ENTRY( 0x0105, NtWow64QueryInformationProcess64, 20 ) \
-    SYSCALL_ENTRY( 0x0106, NtWow64ReadVirtualMemory64, 28 ) \
-    SYSCALL_ENTRY( 0x0107, NtWow64WriteVirtualMemory64, 28 )
+    SYSCALL_ENTRY( 0x00a7, NtListenPort, 8 ) \
+    SYSCALL_ENTRY( 0x00a8, NtLoadDriver, 4 ) \
+    SYSCALL_ENTRY( 0x00a9, NtLoadKey, 8 ) \
+    SYSCALL_ENTRY( 0x00aa, NtLoadKey2, 12 ) \
+    SYSCALL_ENTRY( 0x00ab, NtLoadKeyEx, 32 ) \
+    SYSCALL_ENTRY( 0x00ac, NtLockFile, 40 ) \
+    SYSCALL_ENTRY( 0x00ad, NtLockVirtualMemory, 16 ) \
+    SYSCALL_ENTRY( 0x00ae, NtMakePermanentObject, 4 ) \
+    SYSCALL_ENTRY( 0x00af, NtMakeTemporaryObject, 4 ) \
+    SYSCALL_ENTRY( 0x00b0, NtMapViewOfSectionEx, 36 ) \
+    SYSCALL_ENTRY( 0x00b1, NtNotifyChangeDirectoryFile, 36 ) \
+    SYSCALL_ENTRY( 0x00b2, NtNotifyChangeKey, 40 ) \
+    SYSCALL_ENTRY( 0x00b3, NtNotifyChangeMultipleKeys, 48 ) \
+    SYSCALL_ENTRY( 0x00b4, NtOpenIoCompletion, 12 ) \
+    SYSCALL_ENTRY( 0x00b5, NtOpenJobObject, 12 ) \
+    SYSCALL_ENTRY( 0x00b6, NtOpenKeyEx, 16 ) \
+    SYSCALL_ENTRY( 0x00b7, NtOpenKeyTransacted, 16 ) \
+    SYSCALL_ENTRY( 0x00b8, NtOpenKeyTransactedEx, 20 ) \
+    SYSCALL_ENTRY( 0x00b9, NtOpenKeyedEvent, 12 ) \
+    SYSCALL_ENTRY( 0x00ba, NtOpenMutant, 12 ) \
+    SYSCALL_ENTRY( 0x00bb, NtOpenProcessToken, 12 ) \
+    SYSCALL_ENTRY( 0x00bc, NtOpenSemaphore, 12 ) \
+    SYSCALL_ENTRY( 0x00bd, NtOpenSymbolicLinkObject, 12 ) \
+    SYSCALL_ENTRY( 0x00be, NtOpenThread, 16 ) \
+    SYSCALL_ENTRY( 0x00bf, NtOpenTimer, 12 ) \
+    SYSCALL_ENTRY( 0x00c0, NtPrivilegeCheck, 12 ) \
+    SYSCALL_ENTRY( 0x00c1, NtPulseEvent, 8 ) \
+    SYSCALL_ENTRY( 0x00c2, NtQueryDirectoryObject, 28 ) \
+    SYSCALL_ENTRY( 0x00c3, NtQueryEaFile, 36 ) \
+    SYSCALL_ENTRY( 0x00c4, NtQueryFullAttributesFile, 8 ) \
+    SYSCALL_ENTRY( 0x00c5, NtQueryInformationAtom, 20 ) \
+    SYSCALL_ENTRY( 0x00c6, NtQueryInformationJobObject, 20 ) \
+    SYSCALL_ENTRY( 0x00c7, NtQueryInstallUILanguage, 4 ) \
+    SYSCALL_ENTRY( 0x00c8, NtQueryIoCompletion, 20 ) \
+    SYSCALL_ENTRY( 0x00c9, NtQueryLicenseValue, 20 ) \
+    SYSCALL_ENTRY( 0x00ca, NtQueryMultipleValueKey, 24 ) \
+    SYSCALL_ENTRY( 0x00cb, NtQueryMutant, 20 ) \
+    SYSCALL_ENTRY( 0x00cc, NtQuerySecurityObject, 20 ) \
+    SYSCALL_ENTRY( 0x00cd, NtQuerySemaphore, 20 ) \
+    SYSCALL_ENTRY( 0x00ce, NtQuerySymbolicLinkObject, 12 ) \
+    SYSCALL_ENTRY( 0x00cf, NtQuerySystemEnvironmentValue, 16 ) \
+    SYSCALL_ENTRY( 0x00d0, NtQuerySystemEnvironmentValueEx, 20 ) \
+    SYSCALL_ENTRY( 0x00d1, NtQuerySystemInformationEx, 24 ) \
+    SYSCALL_ENTRY( 0x00d2, NtQueryTimerResolution, 12 ) \
+    SYSCALL_ENTRY( 0x00d3, NtQueueApcThreadEx, 24 ) \
+    SYSCALL_ENTRY( 0x00d4, NtQueueApcThreadEx2, 28 ) \
+    SYSCALL_ENTRY( 0x00d5, NtRaiseException, 12 ) \
+    SYSCALL_ENTRY( 0x00d6, NtRaiseHardError, 24 ) \
+    SYSCALL_ENTRY( 0x00d7, NtRegisterThreadTerminatePort, 4 ) \
+    SYSCALL_ENTRY( 0x00d8, NtReleaseKeyedEvent, 16 ) \
+    SYSCALL_ENTRY( 0x00d9, NtRemoveIoCompletionEx, 24 ) \
+    SYSCALL_ENTRY( 0x00da, NtRemoveProcessDebug, 8 ) \
+    SYSCALL_ENTRY( 0x00db, NtRenameKey, 8 ) \
+    SYSCALL_ENTRY( 0x00dc, NtReplaceKey, 12 ) \
+    SYSCALL_ENTRY( 0x00dd, NtResetEvent, 8 ) \
+    SYSCALL_ENTRY( 0x00de, NtResetWriteWatch, 12 ) \
+    SYSCALL_ENTRY( 0x00df, NtRestoreKey, 12 ) \
+    SYSCALL_ENTRY( 0x00e0, NtResumeProcess, 4 ) \
+    SYSCALL_ENTRY( 0x00e1, NtRollbackTransaction, 8 ) \
+    SYSCALL_ENTRY( 0x00e2, NtSaveKey, 8 ) \
+    SYSCALL_ENTRY( 0x00e3, NtSecureConnectPort, 36 ) \
+    SYSCALL_ENTRY( 0x00e4, NtSetContextThread, 8 ) \
+    SYSCALL_ENTRY( 0x00e5, NtSetDebugFilterState, 12 ) \
+    SYSCALL_ENTRY( 0x00e6, NtSetDefaultLocale, 8 ) \
+    SYSCALL_ENTRY( 0x00e7, NtSetDefaultUILanguage, 4 ) \
+    SYSCALL_ENTRY( 0x00e8, NtSetEaFile, 16 ) \
+    SYSCALL_ENTRY( 0x00e9, NtSetInformationDebugObject, 20 ) \
+    SYSCALL_ENTRY( 0x00ea, NtSetInformationJobObject, 16 ) \
+    SYSCALL_ENTRY( 0x00eb, NtSetInformationKey, 16 ) \
+    SYSCALL_ENTRY( 0x00ec, NtSetInformationToken, 16 ) \
+    SYSCALL_ENTRY( 0x00ed, NtSetInformationVirtualMemory, 24 ) \
+    SYSCALL_ENTRY( 0x00ee, NtSetIntervalProfile, 8 ) \
+    SYSCALL_ENTRY( 0x00ef, NtSetIoCompletion, 20 ) \
+    SYSCALL_ENTRY( 0x00f0, NtSetIoCompletionEx, 24 ) \
+    SYSCALL_ENTRY( 0x00f1, NtSetLdtEntries, 24 ) \
+    SYSCALL_ENTRY( 0x00f2, NtSetSecurityObject, 12 ) \
+    SYSCALL_ENTRY( 0x00f3, NtSetSystemInformation, 12 ) \
+    SYSCALL_ENTRY( 0x00f4, NtSetSystemTime, 8 ) \
+    SYSCALL_ENTRY( 0x00f5, NtSetThreadExecutionState, 8 ) \
+    SYSCALL_ENTRY( 0x00f6, NtSetTimerResolution, 12 ) \
+    SYSCALL_ENTRY( 0x00f7, NtSetVolumeInformationFile, 20 ) \
+    SYSCALL_ENTRY( 0x00f8, NtShutdownSystem, 4 ) \
+    SYSCALL_ENTRY( 0x00f9, NtSignalAndWaitForSingleObject, 16 ) \
+    SYSCALL_ENTRY( 0x00fa, NtSuspendProcess, 4 ) \
+    SYSCALL_ENTRY( 0x00fb, NtSuspendThread, 8 ) \
+    SYSCALL_ENTRY( 0x00fc, NtSystemDebugControl, 24 ) \
+    SYSCALL_ENTRY( 0x00fd, NtTerminateJobObject, 8 ) \
+    SYSCALL_ENTRY( 0x00fe, NtTestAlert, 0 ) \
+    SYSCALL_ENTRY( 0x00ff, NtTraceControl, 24 ) \
+    SYSCALL_ENTRY( 0x0100, NtUnloadDriver, 4 ) \
+    SYSCALL_ENTRY( 0x0101, NtUnloadKey, 4 ) \
+    SYSCALL_ENTRY( 0x0102, NtUnlockFile, 20 ) \
+    SYSCALL_ENTRY( 0x0103, NtUnlockVirtualMemory, 16 ) \
+    SYSCALL_ENTRY( 0x0104, NtUnmapViewOfSectionEx, 12 ) \
+    SYSCALL_ENTRY( 0x0105, NtWaitForAlertByThreadId, 8 ) \
+    SYSCALL_ENTRY( 0x0106, NtWaitForDebugEvent, 16 ) \
+    SYSCALL_ENTRY( 0x0107, NtWaitForKeyedEvent, 16 ) \
+    SYSCALL_ENTRY( 0x0108, NtWow64AllocateVirtualMemory64, 28 ) \
+    SYSCALL_ENTRY( 0x0109, NtWow64GetNativeSystemInformation, 16 ) \
+    SYSCALL_ENTRY( 0x010a, NtWow64IsProcessorFeaturePresent, 4 ) \
+    SYSCALL_ENTRY( 0x010b, NtWow64QueryInformationProcess64, 20 ) \
+    SYSCALL_ENTRY( 0x010c, NtWow64ReadVirtualMemory64, 28 ) \
+    SYSCALL_ENTRY( 0x010d, NtWow64WriteVirtualMemory64, 28 )
 #ifdef _WIN64
 #define ALL_SYSCALLS \
     SYSCALL_ENTRY( 0x0000, NtAccessCheck, 64 ) \
@@ -375,156 +381,162 @@
     SYSCALL_ENTRY( 0x0069, NtAllocateReserveObject, 24 ) \
     SYSCALL_ENTRY( 0x006a, NtAllocateUuids, 32 ) \
     SYSCALL_ENTRY( 0x006b, NtAllocateVirtualMemoryEx, 56 ) \
-    SYSCALL_ENTRY( 0x006c, NtAreMappedFilesTheSame, 16 ) \
-    SYSCALL_ENTRY( 0x006d, NtAssignProcessToJobObject, 16 ) \
-    SYSCALL_ENTRY( 0x006e, NtCancelIoFileEx, 24 ) \
-    SYSCALL_ENTRY( 0x006f, NtCancelSynchronousIoFile, 24 ) \
-    SYSCALL_ENTRY( 0x0070, NtCommitTransaction, 16 ) \
-    SYSCALL_ENTRY( 0x0071, NtCompareObjects, 16 ) \
-    SYSCALL_ENTRY( 0x0072, NtCompareTokens, 24 ) \
-    SYSCALL_ENTRY( 0x0073, NtCompleteConnectPort, 8 ) \
-    SYSCALL_ENTRY( 0x0074, NtConnectPort, 64 ) \
-    SYSCALL_ENTRY( 0x0075, NtContinueEx, 16 ) \
-    SYSCALL_ENTRY( 0x0076, NtConvertBetweenAuxiliaryCounterAndPerformanceCounter, 32 ) \
-    SYSCALL_ENTRY( 0x0077, NtCreateDirectoryObject, 24 ) \
-    SYSCALL_ENTRY( 0x0078, NtCreateIoCompletion, 32 ) \
-    SYSCALL_ENTRY( 0x0079, NtCreateJobObject, 24 ) \
-    SYSCALL_ENTRY( 0x007a, NtCreateKeyTransacted, 64 ) \
-    SYSCALL_ENTRY( 0x007b, NtCreateKeyedEvent, 32 ) \
-    SYSCALL_ENTRY( 0x007c, NtCreateLowBoxToken, 72 ) \
-    SYSCALL_ENTRY( 0x007d, NtCreateMailslotFile, 64 ) \
-    SYSCALL_ENTRY( 0x007e, NtCreateMutant, 32 ) \
-    SYSCALL_ENTRY( 0x007f, NtCreateNamedPipeFile, 112 ) \
-    SYSCALL_ENTRY( 0x0080, NtCreatePagingFile, 32 ) \
-    SYSCALL_ENTRY( 0x0081, NtCreatePort, 40 ) \
-    SYSCALL_ENTRY( 0x0082, NtCreateSectionEx, 72 ) \
-    SYSCALL_ENTRY( 0x0083, NtCreateSemaphore, 40 ) \
-    SYSCALL_ENTRY( 0x0084, NtCreateSymbolicLinkObject, 32 ) \
-    SYSCALL_ENTRY( 0x0085, NtCreateThreadEx, 88 ) \
-    SYSCALL_ENTRY( 0x0086, NtCreateTimer, 32 ) \
-    SYSCALL_ENTRY( 0x0087, NtCreateToken, 104 ) \
-    SYSCALL_ENTRY( 0x0088, NtCreateTransaction, 80 ) \
-    SYSCALL_ENTRY( 0x0089, NtCreateUserProcess, 88 ) \
-    SYSCALL_ENTRY( 0x008a, NtDebugActiveProcess, 16 ) \
-    SYSCALL_ENTRY( 0x008b, NtDebugContinue, 24 ) \
-    SYSCALL_ENTRY( 0x008c, NtDeleteAtom, 8 ) \
-    SYSCALL_ENTRY( 0x008d, NtDeleteFile, 8 ) \
-    SYSCALL_ENTRY( 0x008e, NtDeleteKey, 8 ) \
-    SYSCALL_ENTRY( 0x008f, NtDeleteValueKey, 16 ) \
-    SYSCALL_ENTRY( 0x0090, NtDisplayString, 8 ) \
-    SYSCALL_ENTRY( 0x0091, NtFilterToken, 48 ) \
-    SYSCALL_ENTRY( 0x0092, NtFlushBuffersFileEx, 40 ) \
-    SYSCALL_ENTRY( 0x0093, NtFlushInstructionCache, 24 ) \
-    SYSCALL_ENTRY( 0x0094, NtFlushKey, 8 ) \
-    SYSCALL_ENTRY( 0x0095, NtFlushProcessWriteBuffers, 0 ) \
-    SYSCALL_ENTRY( 0x0096, NtFlushVirtualMemory, 32 ) \
-    SYSCALL_ENTRY( 0x0097, NtGetContextThread, 16 ) \
-    SYSCALL_ENTRY( 0x0098, NtGetCurrentProcessorNumber, 0 ) \
-    SYSCALL_ENTRY( 0x0099, NtGetNextProcess, 40 ) \
-    SYSCALL_ENTRY( 0x009a, NtGetNextThread, 48 ) \
-    SYSCALL_ENTRY( 0x009b, NtGetNlsSectionPtr, 40 ) \
-    SYSCALL_ENTRY( 0x009c, NtGetWriteWatch, 56 ) \
-    SYSCALL_ENTRY( 0x009d, NtImpersonateAnonymousToken, 8 ) \
-    SYSCALL_ENTRY( 0x009e, NtInitializeNlsFiles, 24 ) \
-    SYSCALL_ENTRY( 0x009f, NtInitiatePowerAction, 32 ) \
-    SYSCALL_ENTRY( 0x00a0, NtListenPort, 16 ) \
-    SYSCALL_ENTRY( 0x00a1, NtLoadDriver, 8 ) \
-    SYSCALL_ENTRY( 0x00a2, NtLoadKey, 16 ) \
-    SYSCALL_ENTRY( 0x00a3, NtLoadKey2, 24 ) \
-    SYSCALL_ENTRY( 0x00a4, NtLoadKeyEx, 64 ) \
-    SYSCALL_ENTRY( 0x00a5, NtLockFile, 80 ) \
+    SYSCALL_ENTRY( 0x006c, NtAlpcAcceptConnectPort, 72 ) \
+    SYSCALL_ENTRY( 0x006d, NtAlpcConnectPort, 88 ) \
+    SYSCALL_ENTRY( 0x006e, NtAlpcCreatePort, 24 ) \
+    SYSCALL_ENTRY( 0x006f, NtAlpcDisconnectPort, 16 ) \
+    SYSCALL_ENTRY( 0x0070, NtAlpcImpersonateClientOfPort, 24 ) \
+    SYSCALL_ENTRY( 0x0071, NtAlpcSendWaitReceivePort, 64 ) \
+    SYSCALL_ENTRY( 0x0072, NtAreMappedFilesTheSame, 16 ) \
+    SYSCALL_ENTRY( 0x0073, NtAssignProcessToJobObject, 16 ) \
+    SYSCALL_ENTRY( 0x0074, NtCancelIoFileEx, 24 ) \
+    SYSCALL_ENTRY( 0x0075, NtCancelSynchronousIoFile, 24 ) \
+    SYSCALL_ENTRY( 0x0076, NtCommitTransaction, 16 ) \
+    SYSCALL_ENTRY( 0x0077, NtCompareObjects, 16 ) \
+    SYSCALL_ENTRY( 0x0078, NtCompareTokens, 24 ) \
+    SYSCALL_ENTRY( 0x0079, NtCompleteConnectPort, 8 ) \
+    SYSCALL_ENTRY( 0x007a, NtConnectPort, 64 ) \
+    SYSCALL_ENTRY( 0x007b, NtContinueEx, 16 ) \
+    SYSCALL_ENTRY( 0x007c, NtConvertBetweenAuxiliaryCounterAndPerformanceCounter, 32 ) \
+    SYSCALL_ENTRY( 0x007d, NtCreateDirectoryObject, 24 ) \
+    SYSCALL_ENTRY( 0x007e, NtCreateIoCompletion, 32 ) \
+    SYSCALL_ENTRY( 0x007f, NtCreateJobObject, 24 ) \
+    SYSCALL_ENTRY( 0x0080, NtCreateKeyTransacted, 64 ) \
+    SYSCALL_ENTRY( 0x0081, NtCreateKeyedEvent, 32 ) \
+    SYSCALL_ENTRY( 0x0082, NtCreateLowBoxToken, 72 ) \
+    SYSCALL_ENTRY( 0x0083, NtCreateMailslotFile, 64 ) \
+    SYSCALL_ENTRY( 0x0084, NtCreateMutant, 32 ) \
+    SYSCALL_ENTRY( 0x0085, NtCreateNamedPipeFile, 112 ) \
+    SYSCALL_ENTRY( 0x0086, NtCreatePagingFile, 32 ) \
+    SYSCALL_ENTRY( 0x0087, NtCreatePort, 40 ) \
+    SYSCALL_ENTRY( 0x0088, NtCreateSectionEx, 72 ) \
+    SYSCALL_ENTRY( 0x0089, NtCreateSemaphore, 40 ) \
+    SYSCALL_ENTRY( 0x008a, NtCreateSymbolicLinkObject, 32 ) \
+    SYSCALL_ENTRY( 0x008b, NtCreateThreadEx, 88 ) \
+    SYSCALL_ENTRY( 0x008c, NtCreateTimer, 32 ) \
+    SYSCALL_ENTRY( 0x008d, NtCreateToken, 104 ) \
+    SYSCALL_ENTRY( 0x008e, NtCreateTransaction, 80 ) \
+    SYSCALL_ENTRY( 0x008f, NtCreateUserProcess, 88 ) \
+    SYSCALL_ENTRY( 0x0090, NtDebugActiveProcess, 16 ) \
+    SYSCALL_ENTRY( 0x0091, NtDebugContinue, 24 ) \
+    SYSCALL_ENTRY( 0x0092, NtDeleteAtom, 8 ) \
+    SYSCALL_ENTRY( 0x0093, NtDeleteFile, 8 ) \
+    SYSCALL_ENTRY( 0x0094, NtDeleteKey, 8 ) \
+    SYSCALL_ENTRY( 0x0095, NtDeleteValueKey, 16 ) \
+    SYSCALL_ENTRY( 0x0096, NtDisplayString, 8 ) \
+    SYSCALL_ENTRY( 0x0097, NtFilterToken, 48 ) \
+    SYSCALL_ENTRY( 0x0098, NtFlushBuffersFileEx, 40 ) \
+    SYSCALL_ENTRY( 0x0099, NtFlushInstructionCache, 24 ) \
+    SYSCALL_ENTRY( 0x009a, NtFlushKey, 8 ) \
+    SYSCALL_ENTRY( 0x009b, NtFlushProcessWriteBuffers, 0 ) \
+    SYSCALL_ENTRY( 0x009c, NtFlushVirtualMemory, 32 ) \
+    SYSCALL_ENTRY( 0x009d, NtGetContextThread, 16 ) \
+    SYSCALL_ENTRY( 0x009e, NtGetCurrentProcessorNumber, 0 ) \
+    SYSCALL_ENTRY( 0x009f, NtGetNextProcess, 40 ) \
+    SYSCALL_ENTRY( 0x00a0, NtGetNextThread, 48 ) \
+    SYSCALL_ENTRY( 0x00a1, NtGetNlsSectionPtr, 40 ) \
+    SYSCALL_ENTRY( 0x00a2, NtGetWriteWatch, 56 ) \
+    SYSCALL_ENTRY( 0x00a3, NtImpersonateAnonymousToken, 8 ) \
+    SYSCALL_ENTRY( 0x00a4, NtInitializeNlsFiles, 24 ) \
+    SYSCALL_ENTRY( 0x00a5, NtInitiatePowerAction, 32 ) \
     SYSCALL_ENTRY( 0x00a6, NtCreateDebugObject, 32 ) \
-    SYSCALL_ENTRY( 0x00a7, NtLockVirtualMemory, 32 ) \
-    SYSCALL_ENTRY( 0x00a8, NtMakePermanentObject, 8 ) \
-    SYSCALL_ENTRY( 0x00a9, NtMakeTemporaryObject, 8 ) \
-    SYSCALL_ENTRY( 0x00aa, NtMapViewOfSectionEx, 72 ) \
-    SYSCALL_ENTRY( 0x00ab, NtNotifyChangeDirectoryFile, 72 ) \
-    SYSCALL_ENTRY( 0x00ac, NtNotifyChangeKey, 80 ) \
-    SYSCALL_ENTRY( 0x00ad, NtNotifyChangeMultipleKeys, 96 ) \
-    SYSCALL_ENTRY( 0x00ae, NtOpenIoCompletion, 24 ) \
-    SYSCALL_ENTRY( 0x00af, NtOpenJobObject, 24 ) \
-    SYSCALL_ENTRY( 0x00b0, NtOpenKeyEx, 32 ) \
-    SYSCALL_ENTRY( 0x00b1, NtOpenKeyTransacted, 32 ) \
-    SYSCALL_ENTRY( 0x00b2, NtOpenKeyTransactedEx, 40 ) \
-    SYSCALL_ENTRY( 0x00b3, NtOpenKeyedEvent, 24 ) \
-    SYSCALL_ENTRY( 0x00b4, NtOpenMutant, 24 ) \
-    SYSCALL_ENTRY( 0x00b5, NtOpenProcessToken, 24 ) \
-    SYSCALL_ENTRY( 0x00b6, NtOpenSemaphore, 24 ) \
-    SYSCALL_ENTRY( 0x00b7, NtOpenSymbolicLinkObject, 24 ) \
-    SYSCALL_ENTRY( 0x00b8, NtOpenThread, 32 ) \
-    SYSCALL_ENTRY( 0x00b9, NtOpenTimer, 24 ) \
-    SYSCALL_ENTRY( 0x00ba, NtPrivilegeCheck, 24 ) \
-    SYSCALL_ENTRY( 0x00bb, NtPulseEvent, 16 ) \
-    SYSCALL_ENTRY( 0x00bc, NtQueryDirectoryObject, 56 ) \
-    SYSCALL_ENTRY( 0x00bd, NtQueryEaFile, 72 ) \
-    SYSCALL_ENTRY( 0x00be, NtQueryFullAttributesFile, 16 ) \
-    SYSCALL_ENTRY( 0x00bf, NtQueryInformationAtom, 40 ) \
-    SYSCALL_ENTRY( 0x00c0, NtQueryInformationJobObject, 40 ) \
-    SYSCALL_ENTRY( 0x00c1, NtQueryInstallUILanguage, 8 ) \
-    SYSCALL_ENTRY( 0x00c2, NtQueryIoCompletion, 40 ) \
-    SYSCALL_ENTRY( 0x00c3, NtQueryLicenseValue, 40 ) \
-    SYSCALL_ENTRY( 0x00c4, NtQueryMultipleValueKey, 48 ) \
-    SYSCALL_ENTRY( 0x00c5, NtQueryMutant, 40 ) \
-    SYSCALL_ENTRY( 0x00c6, NtQuerySecurityObject, 40 ) \
-    SYSCALL_ENTRY( 0x00c7, NtQuerySemaphore, 40 ) \
-    SYSCALL_ENTRY( 0x00c8, NtQuerySymbolicLinkObject, 24 ) \
-    SYSCALL_ENTRY( 0x00c9, NtQuerySystemEnvironmentValue, 32 ) \
-    SYSCALL_ENTRY( 0x00ca, NtQuerySystemEnvironmentValueEx, 40 ) \
-    SYSCALL_ENTRY( 0x00cb, NtQuerySystemInformationEx, 48 ) \
-    SYSCALL_ENTRY( 0x00cc, NtQueryTimerResolution, 24 ) \
-    SYSCALL_ENTRY( 0x00cd, NtQueueApcThreadEx, 48 ) \
-    SYSCALL_ENTRY( 0x00ce, NtQueueApcThreadEx2, 56 ) \
-    SYSCALL_ENTRY( 0x00cf, NtRaiseException, 24 ) \
-    SYSCALL_ENTRY( 0x00d0, NtRaiseHardError, 48 ) \
-    SYSCALL_ENTRY( 0x00d1, NtRegisterThreadTerminatePort, 8 ) \
-    SYSCALL_ENTRY( 0x00d2, NtReleaseKeyedEvent, 32 ) \
-    SYSCALL_ENTRY( 0x00d3, NtRemoveIoCompletionEx, 48 ) \
-    SYSCALL_ENTRY( 0x00d4, NtRemoveProcessDebug, 16 ) \
-    SYSCALL_ENTRY( 0x00d5, NtRenameKey, 16 ) \
-    SYSCALL_ENTRY( 0x00d6, NtReplaceKey, 24 ) \
-    SYSCALL_ENTRY( 0x00d7, NtResetEvent, 16 ) \
-    SYSCALL_ENTRY( 0x00d8, NtResetWriteWatch, 24 ) \
-    SYSCALL_ENTRY( 0x00d9, NtRestoreKey, 24 ) \
-    SYSCALL_ENTRY( 0x00da, NtResumeProcess, 8 ) \
-    SYSCALL_ENTRY( 0x00db, NtRollbackTransaction, 16 ) \
-    SYSCALL_ENTRY( 0x00dc, NtSaveKey, 16 ) \
-    SYSCALL_ENTRY( 0x00dd, NtSecureConnectPort, 72 ) \
-    SYSCALL_ENTRY( 0x00de, NtSetContextThread, 16 ) \
-    SYSCALL_ENTRY( 0x00df, NtSetDebugFilterState, 24 ) \
-    SYSCALL_ENTRY( 0x00e0, NtSetDefaultLocale, 16 ) \
-    SYSCALL_ENTRY( 0x00e1, NtSetDefaultUILanguage, 8 ) \
-    SYSCALL_ENTRY( 0x00e2, NtSetEaFile, 32 ) \
-    SYSCALL_ENTRY( 0x00e3, NtSetInformationDebugObject, 40 ) \
-    SYSCALL_ENTRY( 0x00e4, NtSetInformationJobObject, 32 ) \
-    SYSCALL_ENTRY( 0x00e5, NtSetInformationKey, 32 ) \
-    SYSCALL_ENTRY( 0x00e6, NtSetInformationToken, 32 ) \
-    SYSCALL_ENTRY( 0x00e7, NtSetInformationVirtualMemory, 48 ) \
-    SYSCALL_ENTRY( 0x00e8, NtSetIntervalProfile, 16 ) \
-    SYSCALL_ENTRY( 0x00e9, NtSetIoCompletion, 40 ) \
-    SYSCALL_ENTRY( 0x00ea, NtSetIoCompletionEx, 48 ) \
-    SYSCALL_ENTRY( 0x00eb, NtSetLdtEntries, 48 ) \
-    SYSCALL_ENTRY( 0x00ec, NtSetSecurityObject, 24 ) \
-    SYSCALL_ENTRY( 0x00ed, NtSetSystemInformation, 24 ) \
-    SYSCALL_ENTRY( 0x00ee, NtSetSystemTime, 16 ) \
-    SYSCALL_ENTRY( 0x00ef, NtSetThreadExecutionState, 16 ) \
-    SYSCALL_ENTRY( 0x00f0, NtSetTimerResolution, 24 ) \
-    SYSCALL_ENTRY( 0x00f1, NtSetVolumeInformationFile, 40 ) \
-    SYSCALL_ENTRY( 0x00f2, NtShutdownSystem, 8 ) \
-    SYSCALL_ENTRY( 0x00f3, NtSignalAndWaitForSingleObject, 32 ) \
-    SYSCALL_ENTRY( 0x00f4, NtSuspendProcess, 8 ) \
-    SYSCALL_ENTRY( 0x00f5, NtSuspendThread, 16 ) \
-    SYSCALL_ENTRY( 0x00f6, NtSystemDebugControl, 48 ) \
-    SYSCALL_ENTRY( 0x00f7, NtTerminateJobObject, 16 ) \
-    SYSCALL_ENTRY( 0x00f8, NtTestAlert, 0 ) \
-    SYSCALL_ENTRY( 0x00f9, NtTraceControl, 48 ) \
-    SYSCALL_ENTRY( 0x00fa, NtUnloadDriver, 8 ) \
-    SYSCALL_ENTRY( 0x00fb, NtUnloadKey, 8 ) \
-    SYSCALL_ENTRY( 0x00fc, NtUnlockFile, 40 ) \
-    SYSCALL_ENTRY( 0x00fd, NtUnlockVirtualMemory, 32 ) \
-    SYSCALL_ENTRY( 0x00fe, NtUnmapViewOfSectionEx, 24 ) \
-    SYSCALL_ENTRY( 0x00ff, NtWaitForAlertByThreadId, 16 ) \
-    SYSCALL_ENTRY( 0x0100, NtWaitForDebugEvent, 32 ) \
-    SYSCALL_ENTRY( 0x0101, NtWaitForKeyedEvent, 32 )
+    SYSCALL_ENTRY( 0x00a7, NtListenPort, 16 ) \
+    SYSCALL_ENTRY( 0x00a8, NtLoadDriver, 8 ) \
+    SYSCALL_ENTRY( 0x00a9, NtLoadKey, 16 ) \
+    SYSCALL_ENTRY( 0x00aa, NtLoadKey2, 24 ) \
+    SYSCALL_ENTRY( 0x00ab, NtLoadKeyEx, 64 ) \
+    SYSCALL_ENTRY( 0x00ac, NtLockFile, 80 ) \
+    SYSCALL_ENTRY( 0x00ad, NtLockVirtualMemory, 32 ) \
+    SYSCALL_ENTRY( 0x00ae, NtMakePermanentObject, 8 ) \
+    SYSCALL_ENTRY( 0x00af, NtMakeTemporaryObject, 8 ) \
+    SYSCALL_ENTRY( 0x00b0, NtMapViewOfSectionEx, 72 ) \
+    SYSCALL_ENTRY( 0x00b1, NtNotifyChangeDirectoryFile, 72 ) \
+    SYSCALL_ENTRY( 0x00b2, NtNotifyChangeKey, 80 ) \
+    SYSCALL_ENTRY( 0x00b3, NtNotifyChangeMultipleKeys, 96 ) \
+    SYSCALL_ENTRY( 0x00b4, NtOpenIoCompletion, 24 ) \
+    SYSCALL_ENTRY( 0x00b5, NtOpenJobObject, 24 ) \
+    SYSCALL_ENTRY( 0x00b6, NtOpenKeyEx, 32 ) \
+    SYSCALL_ENTRY( 0x00b7, NtOpenKeyTransacted, 32 ) \
+    SYSCALL_ENTRY( 0x00b8, NtOpenKeyTransactedEx, 40 ) \
+    SYSCALL_ENTRY( 0x00b9, NtOpenKeyedEvent, 24 ) \
+    SYSCALL_ENTRY( 0x00ba, NtOpenMutant, 24 ) \
+    SYSCALL_ENTRY( 0x00bb, NtOpenProcessToken, 24 ) \
+    SYSCALL_ENTRY( 0x00bc, NtOpenSemaphore, 24 ) \
+    SYSCALL_ENTRY( 0x00bd, NtOpenSymbolicLinkObject, 24 ) \
+    SYSCALL_ENTRY( 0x00be, NtOpenThread, 32 ) \
+    SYSCALL_ENTRY( 0x00bf, NtOpenTimer, 24 ) \
+    SYSCALL_ENTRY( 0x00c0, NtPrivilegeCheck, 24 ) \
+    SYSCALL_ENTRY( 0x00c1, NtPulseEvent, 16 ) \
+    SYSCALL_ENTRY( 0x00c2, NtQueryDirectoryObject, 56 ) \
+    SYSCALL_ENTRY( 0x00c3, NtQueryEaFile, 72 ) \
+    SYSCALL_ENTRY( 0x00c4, NtQueryFullAttributesFile, 16 ) \
+    SYSCALL_ENTRY( 0x00c5, NtQueryInformationAtom, 40 ) \
+    SYSCALL_ENTRY( 0x00c6, NtQueryInformationJobObject, 40 ) \
+    SYSCALL_ENTRY( 0x00c7, NtQueryInstallUILanguage, 8 ) \
+    SYSCALL_ENTRY( 0x00c8, NtQueryIoCompletion, 40 ) \
+    SYSCALL_ENTRY( 0x00c9, NtQueryLicenseValue, 40 ) \
+    SYSCALL_ENTRY( 0x00ca, NtQueryMultipleValueKey, 48 ) \
+    SYSCALL_ENTRY( 0x00cb, NtQueryMutant, 40 ) \
+    SYSCALL_ENTRY( 0x00cc, NtQuerySecurityObject, 40 ) \
+    SYSCALL_ENTRY( 0x00cd, NtQuerySemaphore, 40 ) \
+    SYSCALL_ENTRY( 0x00ce, NtQuerySymbolicLinkObject, 24 ) \
+    SYSCALL_ENTRY( 0x00cf, NtQuerySystemEnvironmentValue, 32 ) \
+    SYSCALL_ENTRY( 0x00d0, NtQuerySystemEnvironmentValueEx, 40 ) \
+    SYSCALL_ENTRY( 0x00d1, NtQuerySystemInformationEx, 48 ) \
+    SYSCALL_ENTRY( 0x00d2, NtQueryTimerResolution, 24 ) \
+    SYSCALL_ENTRY( 0x00d3, NtQueueApcThreadEx, 48 ) \
+    SYSCALL_ENTRY( 0x00d4, NtQueueApcThreadEx2, 56 ) \
+    SYSCALL_ENTRY( 0x00d5, NtRaiseException, 24 ) \
+    SYSCALL_ENTRY( 0x00d6, NtRaiseHardError, 48 ) \
+    SYSCALL_ENTRY( 0x00d7, NtRegisterThreadTerminatePort, 8 ) \
+    SYSCALL_ENTRY( 0x00d8, NtReleaseKeyedEvent, 32 ) \
+    SYSCALL_ENTRY( 0x00d9, NtRemoveIoCompletionEx, 48 ) \
+    SYSCALL_ENTRY( 0x00da, NtRemoveProcessDebug, 16 ) \
+    SYSCALL_ENTRY( 0x00db, NtRenameKey, 16 ) \
+    SYSCALL_ENTRY( 0x00dc, NtReplaceKey, 24 ) \
+    SYSCALL_ENTRY( 0x00dd, NtResetEvent, 16 ) \
+    SYSCALL_ENTRY( 0x00de, NtResetWriteWatch, 24 ) \
+    SYSCALL_ENTRY( 0x00df, NtRestoreKey, 24 ) \
+    SYSCALL_ENTRY( 0x00e0, NtResumeProcess, 8 ) \
+    SYSCALL_ENTRY( 0x00e1, NtRollbackTransaction, 16 ) \
+    SYSCALL_ENTRY( 0x00e2, NtSaveKey, 16 ) \
+    SYSCALL_ENTRY( 0x00e3, NtSecureConnectPort, 72 ) \
+    SYSCALL_ENTRY( 0x00e4, NtSetContextThread, 16 ) \
+    SYSCALL_ENTRY( 0x00e5, NtSetDebugFilterState, 24 ) \
+    SYSCALL_ENTRY( 0x00e6, NtSetDefaultLocale, 16 ) \
+    SYSCALL_ENTRY( 0x00e7, NtSetDefaultUILanguage, 8 ) \
+    SYSCALL_ENTRY( 0x00e8, NtSetEaFile, 32 ) \
+    SYSCALL_ENTRY( 0x00e9, NtSetInformationDebugObject, 40 ) \
+    SYSCALL_ENTRY( 0x00ea, NtSetInformationJobObject, 32 ) \
+    SYSCALL_ENTRY( 0x00eb, NtSetInformationKey, 32 ) \
+    SYSCALL_ENTRY( 0x00ec, NtSetInformationToken, 32 ) \
+    SYSCALL_ENTRY( 0x00ed, NtSetInformationVirtualMemory, 48 ) \
+    SYSCALL_ENTRY( 0x00ee, NtSetIntervalProfile, 16 ) \
+    SYSCALL_ENTRY( 0x00ef, NtSetIoCompletion, 40 ) \
+    SYSCALL_ENTRY( 0x00f0, NtSetIoCompletionEx, 48 ) \
+    SYSCALL_ENTRY( 0x00f1, NtSetLdtEntries, 48 ) \
+    SYSCALL_ENTRY( 0x00f2, NtSetSecurityObject, 24 ) \
+    SYSCALL_ENTRY( 0x00f3, NtSetSystemInformation, 24 ) \
+    SYSCALL_ENTRY( 0x00f4, NtSetSystemTime, 16 ) \
+    SYSCALL_ENTRY( 0x00f5, NtSetThreadExecutionState, 16 ) \
+    SYSCALL_ENTRY( 0x00f6, NtSetTimerResolution, 24 ) \
+    SYSCALL_ENTRY( 0x00f7, NtSetVolumeInformationFile, 40 ) \
+    SYSCALL_ENTRY( 0x00f8, NtShutdownSystem, 8 ) \
+    SYSCALL_ENTRY( 0x00f9, NtSignalAndWaitForSingleObject, 32 ) \
+    SYSCALL_ENTRY( 0x00fa, NtSuspendProcess, 8 ) \
+    SYSCALL_ENTRY( 0x00fb, NtSuspendThread, 16 ) \
+    SYSCALL_ENTRY( 0x00fc, NtSystemDebugControl, 48 ) \
+    SYSCALL_ENTRY( 0x00fd, NtTerminateJobObject, 16 ) \
+    SYSCALL_ENTRY( 0x00fe, NtTestAlert, 0 ) \
+    SYSCALL_ENTRY( 0x00ff, NtTraceControl, 48 ) \
+    SYSCALL_ENTRY( 0x0100, NtUnloadDriver, 8 ) \
+    SYSCALL_ENTRY( 0x0101, NtUnloadKey, 8 ) \
+    SYSCALL_ENTRY( 0x0102, NtUnlockFile, 40 ) \
+    SYSCALL_ENTRY( 0x0103, NtUnlockVirtualMemory, 32 ) \
+    SYSCALL_ENTRY( 0x0104, NtUnmapViewOfSectionEx, 24 ) \
+    SYSCALL_ENTRY( 0x0105, NtWaitForAlertByThreadId, 16 ) \
+    SYSCALL_ENTRY( 0x0106, NtWaitForDebugEvent, 32 ) \
+    SYSCALL_ENTRY( 0x0107, NtWaitForKeyedEvent, 32 )
 #else
 #define ALL_SYSCALLS ALL_SYSCALLS32
 #endif
diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c
index b6a9aa5f0454..baee1f237221 100644
--- a/dlls/ntdll/signal_arm64ec.c
+++ b/dlls/ntdll/signal_arm64ec.c
@@ -83,7 +83,16 @@ static inline BOOL enter_syscall_callback(void)
 
 static inline void leave_syscall_callback(void)
 {
-    get_arm64ec_cpu_area()->InSyscallCallback = 0;
+    CHPE_V2_CPU_AREA_INFO *cpu_area = get_arm64ec_cpu_area();
+    CONTEXT ctx;
+
+    cpu_area->InSyscallCallback = 0;
+
+    if (cpu_area->SuspendDoorbell && *cpu_area->SuspendDoorbell)
+    {
+        RtlCaptureContext( &ctx );
+        if (*cpu_area->SuspendDoorbell) NtContinue( &ctx, FALSE );
+    }
 }
 
 /**********************************************************************
@@ -355,6 +364,12 @@ DEFINE_SYSCALL(NtAllocateReserveObject, (HANDLE *handle, const OBJECT_ATTRIBUTES
 DEFINE_SYSCALL(NtAllocateUuids, (ULARGE_INTEGER *time, ULONG *delta, ULONG *sequence, UCHAR *seed))
 DEFINE_WRAPPED_SYSCALL(NtAllocateVirtualMemory, (HANDLE process, PVOID *ret, ULONG_PTR zero_bits, SIZE_T *size_ptr, ULONG type, ULONG protect))
 DEFINE_WRAPPED_SYSCALL(NtAllocateVirtualMemoryEx, (HANDLE process, PVOID *ret, SIZE_T *size_ptr, ULONG type, ULONG protect, MEM_EXTENDED_PARAMETER *parameters, ULONG count))
+DEFINE_SYSCALL(NtAlpcAcceptConnectPort, (HANDLE *communication_port, HANDLE connection_port, DWORD flags, OBJECT_ATTRIBUTES *obj_attr, ALPC_PORT_ATTRIBUTES *port_attr, void *port_context, ALPC_PORT_MESSAGE *send_msg, ALPC_MESSAGE_ATTRIBUTES *send_msg_attr, BOOLEAN accept))
+DEFINE_SYSCALL(NtAlpcConnectPort, (HANDLE *port_handle, UNICODE_STRING *port_name, OBJECT_ATTRIBUTES *obj_attr, ALPC_PORT_ATTRIBUTES *port_attr, DWORD flags, PSID required_server_sid, ALPC_PORT_MESSAGE *connect_msg, SIZE_T *connect_msg_size, ALPC_MESSAGE_ATTRIBUTES *send_msg_attr, ALPC_MESSAGE_ATTRIBUTES *recv_msg_attr, LARGE_INTEGER *timeout))
+DEFINE_SYSCALL(NtAlpcCreatePort, (HANDLE *port_handle, OBJECT_ATTRIBUTES *obj_attr, ALPC_PORT_ATTRIBUTES *port_attr))
+DEFINE_SYSCALL(NtAlpcDisconnectPort, (HANDLE port_handle, ULONG flags))
+DEFINE_SYSCALL(NtAlpcImpersonateClientOfPort, (HANDLE port_handle, ALPC_PORT_MESSAGE *msg, void *reserved ))
+DEFINE_SYSCALL(NtAlpcSendWaitReceivePort, (HANDLE port_handle, DWORD flags, ALPC_PORT_MESSAGE *send_msg, ALPC_MESSAGE_ATTRIBUTES *send_msg_attr, ALPC_PORT_MESSAGE *recv_msg, SIZE_T *recv_buffer_size, ALPC_MESSAGE_ATTRIBUTES *recv_msg_attr, LARGE_INTEGER *timeout))
 DEFINE_SYSCALL(NtApphelpCacheControl, (ULONG class, void *context))
 DEFINE_SYSCALL(NtAreMappedFilesTheSame, (PVOID addr1, PVOID addr2))
 DEFINE_SYSCALL(NtAssignProcessToJobObject, (HANDLE job, HANDLE process))
@@ -804,12 +819,15 @@ NTSTATUS SYSCALL_API NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
     if (pBTCpu64NotifyReadFile && enter_syscall_callback())
     {
         pBTCpu64NotifyReadFile( handle, buffer, length, FALSE, 0 );
-        status = syscall_NtReadFile( handle, event, apc, apc_user, io, buffer, length, offset, key );
-        if (pBTCpu64NotifyReadFile) pBTCpu64NotifyReadFile( handle, buffer, length, TRUE, status );
         leave_syscall_callback();
-        return status;
     }
-    return syscall_NtReadFile( handle, event, apc, apc_user, io, buffer, length, offset, key );
+    status = syscall_NtReadFile( handle, event, apc, apc_user, io, buffer, length, offset, key );
+    if (pBTCpu64NotifyReadFile && enter_syscall_callback())
+    {
+        pBTCpu64NotifyReadFile( handle, buffer, length, TRUE, status );
+        leave_syscall_callback();
+    }
+    return status;
 }
 
 NTSTATUS SYSCALL_API NtSetContextThread( HANDLE handle, const CONTEXT *context )
diff --git a/dlls/ntdll/tests/wow64.c b/dlls/ntdll/tests/wow64.c
index a88c3db031de..1fa05fdbacd7 100644
--- a/dlls/ntdll/tests/wow64.c
+++ b/dlls/ntdll/tests/wow64.c
@@ -3215,12 +3215,18 @@ static void test_exception_dispatcher(void)
 
 #ifdef __arm64ec__
 
-static ULONG *doorbell;
-static ULONG64 suspend_rip;
+struct doorbell_params
+{
+    ULONG *doorbell;
+    ULONG64 suspend_rip;
+    BOOL syscall;
+    BOOL suspend;
+};
 
 static DWORD WINAPI doorbell_thread( void *arg )
 {
     CHPE_V2_CPU_AREA_INFO *chpe = NtCurrentTeb()->ChpeV2CpuAreaInfo;
+    struct doorbell_params *params = arg;
     ULONG signaled_doorbell = -1;
     NTSTATUS status;
     HANDLE event;
@@ -3231,13 +3237,14 @@ static DWORD WINAPI doorbell_thread( void *arg )
 
     if (InterlockedIncrement( &i ) == 1)
     {
-        suspend_rip = ctx.Rip;
+        params->suspend_rip = ctx.Rip;
 
-        chpe->InSimulation = 1;
-        doorbell = chpe->SuspendDoorbell;
-        ok( doorbell != NULL, "doorbell is not available\n" );
-        while (!(signaled_doorbell = *doorbell)) YieldProcessor();
-        chpe->InSimulation = 0;
+        if (params->syscall) chpe->InSyscallCallback = 1;
+        else chpe->InSimulation = 1;
+        params->doorbell = chpe->SuspendDoorbell;
+        ok( params->doorbell != NULL, "doorbell is not available\n" );
+        while (!(signaled_doorbell = *params->doorbell)) YieldProcessor();
+        chpe->InSyscallCallback = chpe->InSimulation = 0;
 
         /* syscalls, including waits, continue working */
         event = CreateEventW( NULL, FALSE, TRUE, NULL );
@@ -3251,41 +3258,164 @@ static DWORD WINAPI doorbell_thread( void *arg )
         ok( 0, "NtContinue failed\n" );
     }
 
-    ok( !*doorbell, "doorbell = %lx\n", *doorbell );
+    ok( !*params->doorbell, "doorbell = %lx\n", *params->doorbell );
     ok( signaled_doorbell == -1, "signaled_doorbell = %lx\n", signaled_doorbell );
-    doorbell = NULL;
+    params->doorbell = NULL;
+    return 0;
+}
+
+struct pipe_read_params
+{
+    CHPE_V2_CPU_AREA_INFO *chpe;
+    HANDLE pipe;
+    HANDLE event;
+    int flush;
+};
+
+static DWORD WINAPI pipe_read_thread( void *arg )
+{
+    struct pipe_read_params *params = arg;
+    IO_STATUS_BLOCK iosb;
+    NTSTATUS status;
+    char c;
+
+    params->chpe = NtCurrentTeb()->ChpeV2CpuAreaInfo;
+    NtSetEvent( params->event, NULL );
+    if (params->flush)
+    {
+        int i;
+        for (i = 0; i < 100; i++) NtFlushInstructionCache( GetCurrentProcess, pipe_read_thread, 4 );
+    }
+    status = NtReadFile( params->pipe, NULL, NULL, NULL, &iosb, &c, sizeof(c), NULL, 0 );
+    ok( !status, "NtReadFile failed: %lx\n", status );
+    return 0;
+}
+
+struct nested_continue_params
+{
+    HANDLE event;
+    ULONG64 suspend_rip;
+    LONG pass;
+};
+
+static DWORD WINAPI nested_continue_thread( void *arg )
+{
+    CHPE_V2_CPU_AREA_INFO *chpe = NtCurrentTeb()->ChpeV2CpuAreaInfo;
+    struct nested_continue_params *params = arg;
+    CONTEXT ctx, nested_ctx;
+
+    RtlCaptureContext( &ctx );
+    if (InterlockedIncrement( &params->pass ) != 1) return 0;
+
+    params->suspend_rip = ctx.Rip;
+    chpe->InSyscallCallback = 1;
+    NtSetEvent( params->event, NULL );
+    while (!*chpe->SuspendDoorbell) YieldProcessor();
+
+    /* with InSyscallCallback set, NtContinue does not suspend */
+    RtlCaptureContext( &nested_ctx );
+    if (InterlockedIncrement( &params->pass ) == 2) NtContinue( &nested_ctx, FALSE );
+    chpe->InSyscallCallback = 0;
+
+    /* with InSimulation set, NtContinue does not suspend */
+    chpe->InSimulation = 1;
+    RtlCaptureContext( &nested_ctx );
+    if (InterlockedIncrement( &params->pass ) == 4) NtContinue( &nested_ctx, FALSE );
+    chpe->InSimulation = 0;
+
+    NtContinue( &ctx, FALSE );
     return 0;
 }
 
 static void test_suspend_doorbell(void)
 {
-    HANDLE thread;
+    struct nested_continue_params nested_params;
+    struct pipe_read_params read_params;
+    struct doorbell_params params;
+    HANDLE thread, pipe;
     CONTEXT ctx;
-    int suspend;
+    DWORD ret, pass;
+    char c = 0;
 
-    for (suspend = 0; suspend < 2; suspend++)
+    for (pass = 0; pass < 4; pass++)
     {
-        doorbell = NULL;
-        suspend_rip = 0;
+        memset( &params, 0, sizeof(params) );
+        params.suspend = (pass & 1) != 0;
+        params.syscall = (pass & 2) != 0;
 
-        thread = CreateThread( NULL, 0, doorbell_thread, NULL, 0, NULL );
+        thread = CreateThread( NULL, 0, doorbell_thread, &params, 0, NULL );
         ok( thread != NULL, "CreateThread failed\n" );
 
-        while (!doorbell) YieldProcessor();
-        ok( !*doorbell, "doorbell = %lx\n", *doorbell );
+        while (!params.doorbell) YieldProcessor();
+        ok( !*params.doorbell, "doorbell = %lx\n", *params.doorbell );
 
-        if (suspend) SuspendThread( thread );
+        if (params.suspend) SuspendThread( thread );
 
         memset( &ctx, 0xcc, sizeof(ctx) );
         ctx.ContextFlags = CONTEXT_FULL;
         GetThreadContext( thread, &ctx );
-        ok( ctx.Rip == suspend_rip, "Rip = %llx, expected %llx\n", ctx.Rip, suspend_rip );
+        ok( ctx.Rip == params.suspend_rip, "Rip = %llx, expected %llx\n", ctx.Rip, params.suspend_rip );
 
-        if (suspend) ResumeThread( thread );
+        if (params.suspend) ResumeThread( thread );
 
         WaitForSingleObject( thread, INFINITE );
-        ok( !doorbell, "thread did not reset doorbell\n" );
+        ok( !params.doorbell, "thread did not reset doorbell\n" );
+
+        CloseHandle( thread );
+    }
+
+    for (read_params.flush = 0; read_params.flush < 2; read_params.flush++)
+    {
+        CreatePipe( &read_params.pipe, &pipe, NULL, 0 );
+        read_params.event = CreateEventW( NULL, FALSE, FALSE, NULL );
+        thread = CreateThread( NULL, 0, pipe_read_thread, &read_params, 0, NULL );
+
+        ret = WaitForSingleObject( read_params.event, 10000 );
+        ok( ret == 0, "wait failed %lx\n", ret );
+
+        /* hammer the thread with suspend requests, making sure we never hit a syscall callback */
+        for (pass = 0; pass < 100; pass++)
+        {
+            ret = SuspendThread( thread );
+            ok( !ret, "SuspendThread failed: %lu\n", GetLastError() );
+            ctx.ContextFlags = CONTEXT_FULL;
+            ret = GetThreadContext( thread, &ctx );
+            ok( ret, "GetThreadContext failed: %lu\n", GetLastError() );
+
+            ok( !read_params.chpe->InSyscallCallback, "InSyscallCallback = %x\n",
+                read_params.chpe->InSyscallCallback );
+
+            ret = ResumeThread( thread );
+            ok( ret == 1, "ResumeThread failed: %lu\n", GetLastError() );
+        }
+
+        WriteFile( pipe, &c, sizeof(c), NULL, NULL );
+        ret = WaitForSingleObject( thread, 10000 );
+        ok( ret == 0, "wait failed %lx\n", ret );
+
+        CloseHandle( thread );
+        CloseHandle( pipe );
+        CloseHandle( read_params.pipe );
+        CloseHandle( read_params.event );
     }
+
+    nested_params.event = CreateEventW( NULL, FALSE, FALSE, NULL );
+    nested_params.pass = 0;
+    thread = CreateThread( NULL, 0, nested_continue_thread, &nested_params, 0, NULL );
+
+    ret = WaitForSingleObject( nested_params.event, 10000 );
+    ok( ret == 0, "wait failed %lx\n", ret );
+
+    ctx.ContextFlags = CONTEXT_FULL;
+    GetThreadContext( thread, &ctx );
+    ok( ctx.Rip == nested_params.suspend_rip, "Rip = %llx, expected %llx\n", ctx.Rip, params.suspend_rip );
+
+    ret = WaitForSingleObject( thread, 10000 );
+    ok( ret == 0, "wait failed %lx\n", ret );
+    ok( nested_params.pass == 6, "pass = %lu\n", nested_params.pass );
+
+    CloseHandle( nested_params.event );
+    CloseHandle( thread );
 }
 
 #endif /* __arm64ec__ */
diff --git a/dlls/ntdll/unix/alpc.c b/dlls/ntdll/unix/alpc.c
new file mode 100644
index 000000000000..c708b5880111
--- /dev/null
+++ b/dlls/ntdll/unix/alpc.c
@@ -0,0 +1,83 @@
+/*
+ * Advanced Local Procedure Call
+ *
+ * Copyright 2026 Zhiyi Zhang 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 "ntstatus.h"
+#include "wine/debug.h"
+#include "unix_private.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(alpc);
+
+NTSTATUS WINAPI NtAlpcAcceptConnectPort( HANDLE *communication_port, HANDLE connection_port,
+                                         DWORD flags, OBJECT_ATTRIBUTES *obj_attr,
+                                         ALPC_PORT_ATTRIBUTES *port_attr, void *port_context,
+                                         ALPC_PORT_MESSAGE *send_msg,
+                                         ALPC_MESSAGE_ATTRIBUTES *send_msg_attr, BOOLEAN accept )
+{
+    FIXME( "%p, %p, %#x, %p, %p, %p, %p, %p, %d stub!\n", communication_port, connection_port,
+           (unsigned int)flags, obj_attr, port_attr, port_context, send_msg, send_msg_attr, accept );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS WINAPI NtAlpcConnectPort( HANDLE *port_handle, UNICODE_STRING *port_name,
+                                   OBJECT_ATTRIBUTES *obj_attr, ALPC_PORT_ATTRIBUTES *port_attr,
+                                   DWORD flags, PSID required_server_sid,
+                                   ALPC_PORT_MESSAGE *connect_msg, SIZE_T *connect_msg_size,
+                                   ALPC_MESSAGE_ATTRIBUTES *send_msg_attr,
+                                   ALPC_MESSAGE_ATTRIBUTES *recv_msg_attr, LARGE_INTEGER *timeout )
+{
+    FIXME( "%p, %s, %p, %p, %#x, %p, %p, %p, %p, %p, %p stub!\n", port_handle,
+           debugstr_us( port_name ), obj_attr, port_attr, (unsigned int)flags, required_server_sid,
+           connect_msg, connect_msg_size, send_msg_attr, recv_msg_attr, timeout );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS WINAPI NtAlpcCreatePort( HANDLE *port_handle, OBJECT_ATTRIBUTES *obj_attr, ALPC_PORT_ATTRIBUTES *port_attr )
+{
+    FIXME( "%p, %p, %p stub!\n", port_handle, obj_attr, port_attr );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS WINAPI NtAlpcDisconnectPort( HANDLE port_handle, ULONG flags )
+{
+    FIXME( "%p, %#x stub!\n", port_handle, (unsigned int)flags );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS WINAPI NtAlpcSendWaitReceivePort( HANDLE port_handle, ULONG flags,
+                                           ALPC_PORT_MESSAGE *send_msg,
+                                           ALPC_MESSAGE_ATTRIBUTES *send_msg_attr,
+                                           ALPC_PORT_MESSAGE *recv_msg, SIZE_T *recv_buffer_size,
+                                           ALPC_MESSAGE_ATTRIBUTES *recv_msg_attr,
+                                           LARGE_INTEGER *timeout )
+{
+    FIXME( "%p, %#x, %p, %p, %p, %p, %p, %p stub!\n", port_handle, (unsigned int)flags, send_msg,
+           send_msg_attr, recv_msg, recv_buffer_size, recv_msg_attr, timeout );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS WINAPI NtAlpcImpersonateClientOfPort( HANDLE port_handle, ALPC_PORT_MESSAGE *msg, void *reserved )
+{
+    FIXME( "%p, %p, %p stub!\n", port_handle, msg, reserved );
+    return STATUS_NOT_IMPLEMENTED;
+}
diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c
index c1e6b588dcb6..c1321ce679dd 100644
--- a/dlls/ntdll/unix/loader.c
+++ b/dlls/ntdll/unix/loader.c
@@ -77,9 +77,6 @@
 #else
   extern char **environ;
 #endif
-#ifdef __ANDROID__
-# include <jni.h>
-#endif
 
 #include "ntstatus.h"
 #include "windef.h"
@@ -1874,109 +1871,6 @@ static void start_main_thread(void)
     server_init_process_done();
 }
 
-#ifdef __ANDROID__
-
-#ifndef WINE_JAVA_CLASS
-#define WINE_JAVA_CLASS "org/winehq/wine/WineActivity"
-#endif
-
-DECLSPEC_EXPORT JavaVM *java_vm = NULL;
-DECLSPEC_EXPORT jobject java_object = 0;
-DECLSPEC_EXPORT unsigned short java_gdt_sel = 0;
-
-/* main Wine initialisation */
-static jstring wine_init_jni( JNIEnv *env, jobject obj, jobjectArray cmdline )
-{
-    char **argv;
-    char *str;
-    char error[1024], *winedebuglog = NULL;
-    int i, argc, length;
-    void (*update_func)( const char * );
-
-    /* get the command line array */
-
-    argc = (*env)->GetArrayLength( env, cmdline );
-    for (i = length = 0; i < argc; i++)
-    {
-        jobject str_obj = (*env)->GetObjectArrayElement( env, cmdline, i );
-        length += (*env)->GetStringUTFLength( env, str_obj ) + 1;
-    }
-
-    argv = malloc( (argc + 1) * sizeof(*argv) + length );
-    str = (char *)(argv + argc + 1);
-    for (i = 0; i < argc; i++)
-    {
-        jobject str_obj = (*env)->GetObjectArrayElement( env, cmdline, i );
-        length = (*env)->GetStringUTFLength( env, str_obj );
-        (*env)->GetStringUTFRegion( env, str_obj, 0,
-                                    (*env)->GetStringLength( env, str_obj ), str );
-        argv[i] = str;
-        str[length] = 0;
-        str += length + 1;
-    }
-    argv[argc] = NULL;
-
-    /* set the environment variables */
-
-    // Activity always modifies LD_LIBRARY_PATH in order to load libraries
-    // from custom location in JVM process.
-    update_func = dlsym( RTLD_DEFAULT, "android_update_LD_LIBRARY_PATH" );
-    if (update_func) update_func( getenv("LD_LIBRARY_PATH") );
-
-    winedebuglog = getenv("WINEDEBUGLOG");
-    if (winedebuglog)
-    {
-        int fd = open( winedebuglog, O_WRONLY | O_CREAT | O_APPEND, 0666 );
-        if (fd != -1)
-        {
-            dup2( fd, 2 );
-            close( fd );
-        }
-    }
-
-    java_object = (*env)->NewGlobalRef( env, obj );
-
-    main_argc = argc;
-    main_argv = argv;
-
-    init_paths();
-    init_environment();
-
-#ifdef __i386__
-    {
-        unsigned short java_fs;
-        __asm__( "mov %%fs,%0" : "=r" (java_fs) );
-        if (!(java_fs & 4)) java_gdt_sel = java_fs;
-        __asm__( "mov %0,%%fs" :: "r" (0) );
-        start_main_thread();
-        __asm__( "mov %0,%%fs" :: "r" (java_fs) );
-    }
-#else
-    start_main_thread();
-#endif
-    return (*env)->NewStringUTF( env, error );
-}
-
-jint JNI_OnLoad( JavaVM *vm, void *reserved )
-{
-    static const JNINativeMethod method =
-    {
-        "wine_init", "([Ljava/lang/String;)Ljava/lang/String;", wine_init_jni
-    };
-
-    JNIEnv *env;
-    jclass class;
-
-    virtual_init();
-
-    java_vm = vm;
-    if ((*vm)->AttachCurrentThread( vm, &env, NULL ) != JNI_OK) return JNI_ERR;
-    if (!(class = (*env)->FindClass( env, WINE_JAVA_CLASS ))) return JNI_ERR;
-    (*env)->RegisterNatives( env, class, &method, 1 );
-    return JNI_VERSION_1_6;
-}
-
-#endif  /* __ANDROID__ */
 
 #ifdef __APPLE__
 static void *apple_wine_thread( void *arg )
diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c
index 21714d6d79b2..5a56c2d7d64a 100644
--- a/dlls/ntdll/unix/signal_arm64.c
+++ b/dlls/ntdll/unix/signal_arm64.c
@@ -207,6 +207,8 @@ C_ASSERT( sizeof( struct syscall_frame ) == 0x330 );
 #define ESR_ELx_EC_BRK64                0x3c
 #define ESR_ELx_ISS_DABT_WNR(esr)       (((esr) >> 6) & 0x01)
 #define ESR_ELx_ISS_BRK_COMMENT(esr)    ((esr) & 0xffff)
+#define ESR_ELx_ISS_DFSC(esr)           ((esr) & 0x3f)
+#define ESR_ELx_ISS_DFSC_ALIGN_FAULT    0x21
 
 static DWORD64 make_esr( ULONG ec, ULONG info )
 {
@@ -345,13 +347,14 @@ NTSTATUS signal_set_full_context( CONTEXT *context )
     struct thread_data *data = get_thread_data();
     struct syscall_frame *frame = get_syscall_frame( data );
     struct arm64_thread_data *arm64_data = arm64_thread_data( data );
+    CHPE_V2_CPU_AREA_INFO *cpu_area = data->teb->ChpeV2CpuAreaInfo;
     NTSTATUS status;
 
-    if (arm64_data->suspend_pending)
+    if (arm64_data->suspend_pending && !cpu_area->InSyscallCallback && !cpu_area->InSimulation)
     {
         sigset_t old_set;
         pthread_sigmask( SIG_BLOCK, &server_block_set, &old_set );
-        *data->teb->ChpeV2CpuAreaInfo->SuspendDoorbell = 0;
+        *cpu_area->SuspendDoorbell = 0;
         arm64_data->suspend_pending = FALSE;
         wait_suspend( context );
         status = NtSetContextThread( GetCurrentThread(), context );
@@ -1098,7 +1101,7 @@ static BOOL handle_syscall_fault( struct thread_data *data, ucontext_t *context,
 /**********************************************************************
  *		segv_handler
  *
- * Handler for SIGSEGV.
+ * Handler for SIGSEGV and related errors.
  */
 static void segv_handler( int signal, siginfo_t *siginfo, void *_sigcontext )
 {
@@ -1117,6 +1120,14 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *_sigcontext )
         break;
     case ESR_ELx_EC_DABT_LOW:
     case ESR_ELx_EC_DABT_CUR:
+        if (ESR_ELx_ISS_DFSC(esr) == ESR_ELx_ISS_DFSC_ALIGN_FAULT)
+        {
+            rec.ExceptionCode = EXCEPTION_DATATYPE_MISALIGNMENT;
+            save_context( &context, sigcontext );
+            setup_raise_exception( data, sigcontext, &rec, &context );
+            return;
+        }
+
         if (ESR_ELx_ISS_DABT_WNR(esr))
             rec.ExceptionInformation[0] = EXCEPTION_WRITE_FAULT;
         else
@@ -1169,24 +1180,6 @@ static void ill_handler( int signal, siginfo_t *siginfo, void *_sigcontext )
 }
 
 
-/**********************************************************************
- *		bus_handler
- *
- * Handler for SIGBUS.
- */
-static void bus_handler( int signal, siginfo_t *siginfo, void *_sigcontext )
-{
-    ucontext_t *sigcontext = _sigcontext;
-    struct thread_data *data = get_thread_data();
-    CONTEXT context;
-    EXCEPTION_RECORD rec = { .ExceptionCode = EXCEPTION_DATATYPE_MISALIGNMENT,
-                             .ExceptionAddress = (void *)PC_sig(sigcontext) };
-
-    save_context( &context, sigcontext );
-    setup_raise_exception( data, sigcontext, &rec, &context );
-}
-
-
 /**********************************************************************
  *		trap_handler
  *
@@ -1381,7 +1374,8 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *_sigcontext )
     {
         server_select( NULL, 0, SELECT_INTERRUPTIBLE, 0, NULL, NULL );
     }
-    else if ((chpe = data->teb->ChpeV2CpuAreaInfo) && chpe->InSimulation && chpe->SuspendDoorbell)
+    else if ((chpe = data->teb->ChpeV2CpuAreaInfo) && chpe->SuspendDoorbell &&
+             (chpe->InSimulation || chpe->InSyscallCallback))
     {
         NTSTATUS status = server_select( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_COOPERATIVE_SUSPEND,
                                          0, NULL, NULL );
@@ -1502,11 +1496,10 @@ void signal_init_process( TEB *teb )
     sig_act.sa_sigaction = trap_handler;
     if (sigaction( SIGTRAP, &sig_act, NULL ) == -1) goto error;
     sig_act.sa_sigaction = segv_handler;
+    if (sigaction( SIGBUS, &sig_act, NULL ) == -1) goto error;
     if (sigaction( SIGSEGV, &sig_act, NULL ) == -1) goto error;
     sig_act.sa_sigaction = ill_handler;
     if (sigaction( SIGILL, &sig_act, NULL ) == -1) goto error;
-    sig_act.sa_sigaction = bus_handler;
-    if (sigaction( SIGBUS, &sig_act, NULL ) == -1) goto error;
     return;
 
  error:
diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c
index 56e378cf7706..11a578a75457 100644
--- a/dlls/ntoskrnl.exe/pnp.c
+++ b/dlls/ntoskrnl.exe/pnp.c
@@ -379,12 +379,13 @@ static void start_device( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVINFO_DATA *
     create_dyn_data_key( device );
 }
 
-static void enumerate_new_device( DEVICE_OBJECT *device, HDEVINFO set )
+static void enumerate_new_device( DEVICE_OBJECT *device, HDEVINFO set, DEVICE_OBJECT *parent_device )
 {
     static const WCHAR infpathW[] = {'I','n','f','P','a','t','h',0};
 
     SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
     WCHAR device_instance_id[MAX_DEVICE_ID_LEN];
+    WCHAR parent_id[MAX_DEVICE_ID_LEN];
     DEVICE_CAPABILITIES caps;
     BOOL need_driver = TRUE;
     NTSTATUS status;
@@ -434,6 +435,10 @@ static void enumerate_new_device( DEVICE_OBJECT *device, HDEVINFO set )
         ExFreePool( id );
     }
 
+    if (!get_device_instance_id( parent_device, parent_id ))
+        SetupDiSetDevicePropertyW( set, &sp_device, &DEVPKEY_Device_Parent, DEVPROP_TYPE_STRING,
+                (BYTE *)parent_id, (wcslen( parent_id ) + 1) * sizeof(WCHAR), 0 );
+
     if (need_driver && !install_device_driver( device, set, &sp_device ) && !caps.RawDeviceOK)
     {
         ERR("Unable to install a function driver for device %s.\n", debugstr_w(device_instance_id));
@@ -489,12 +494,16 @@ static void handle_bus_relations( DEVICE_OBJECT *parent )
 {
     struct wine_device *wine_parent = CONTAINING_RECORD(parent, struct wine_device, device_obj);
     SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
+    SP_DEVINFO_DATA parent_sp = {sizeof(parent_sp)};
+    WCHAR parent_id[MAX_DEVICE_ID_LEN];
+    WCHAR (*child_ids)[MAX_DEVICE_ID_LEN] = NULL;
     DEVICE_RELATIONS *relations;
     IO_STATUS_BLOCK irp_status;
     IO_STACK_LOCATION *irpsp;
     HDEVINFO set;
     KEVENT event;
     IRP *irp;
+    DWORD count = 0;
     ULONG i;
 
     TRACE( "(%p)\n", parent );
@@ -535,7 +544,7 @@ static void handle_bus_relations( DEVICE_OBJECT *parent )
         if (!wine_parent->children || !device_in_list( wine_parent->children, child ))
         {
             TRACE("Adding new device %p.\n", child);
-            enumerate_new_device( child, set );
+            enumerate_new_device( child, set, parent );
         }
     }
 
@@ -557,6 +566,65 @@ static void handle_bus_relations( DEVICE_OBJECT *parent )
     ExFreePool( wine_parent->children );
     wine_parent->children = relations;
 
+    count = relations->Count;
+    child_ids = malloc( count * sizeof(*child_ids) );
+
+    for (i = 0; i < count; ++i)
+        get_device_instance_id( relations->Objects[i], child_ids[i] );
+
+    if (count && !get_device_instance_id( parent, parent_id )
+            && SetupDiOpenDeviceInfoW( set, parent_id, NULL, 0, &parent_sp ))
+    {
+        DWORD multi_len = 1;
+        WCHAR *children_multi, *p;
+
+        for (i = 0; i < count; ++i)
+            multi_len += wcslen( child_ids[i] ) + 1;
+
+        children_multi = malloc( multi_len * sizeof(WCHAR) );
+        p = children_multi;
+        for (i = 0; i < count; ++i)
+        {
+            wcscpy( p, child_ids[i] );
+            p += wcslen( child_ids[i] ) + 1;
+        }
+        *p = 0;
+        SetupDiSetDevicePropertyW( set, &parent_sp, &DEVPKEY_Device_Children,
+                DEVPROP_TYPE_STRING_LIST, (BYTE *)children_multi,
+                multi_len * sizeof(WCHAR), 0 );
+        free( children_multi );
+    }
+
+    for (i = 0; i < count; ++i)
+    {
+        SP_DEVINFO_DATA child_sp = {sizeof(child_sp)};
+        if (SetupDiOpenDeviceInfoW( set, child_ids[i], NULL, 0, &child_sp ))
+        {
+            DWORD sib_len = 1, j;
+            WCHAR *siblings_multi, *p;
+
+            for (j = 0; j < count; ++j)
+                if (j != i) sib_len += wcslen( child_ids[j] ) + 1;
+
+            siblings_multi = malloc( sib_len * sizeof(WCHAR) );
+            p = siblings_multi;
+            for (j = 0; j < count; ++j)
+            {
+                if (j != i)
+                {
+                    wcscpy( p, child_ids[j] );
+                    p += wcslen( child_ids[j] ) + 1;
+                }
+            }
+            *p = 0;
+            SetupDiSetDevicePropertyW( set, &child_sp, &DEVPKEY_Device_Siblings,
+                    DEVPROP_TYPE_STRING_LIST, (BYTE *)siblings_multi,
+                    sib_len * sizeof(WCHAR), 0 );
+            free( siblings_multi );
+        }
+    }
+    free( child_ids );
+
     SetupDiDestroyDeviceInfoList( set );
 }
 
diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
index 0c1361dca110..e5b62fa5e6bd 100644
--- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
@@ -2236,6 +2236,80 @@ static void test_pnp_devices(void)
         ok(!wcscmp(buffer_w, expect_device_location_w), "Got device location info %s.\n", debugstr_w(buffer_w));
     }
 
+    /* DEVPKEY_Device_Parent — should be set by ntoskrnl during bus enumeration. */
+    prop_type = DEVPROP_TYPE_EMPTY;
+    size = 0;
+    memset(buffer_w, 0, sizeof(buffer_w));
+    ret = SetupDiGetDevicePropertyW(set, &device, &DEVPKEY_Device_Parent, &prop_type, (BYTE *)buffer_w,
+                                    sizeof(buffer_w), &size, 0);
+    ok(ret, "Got error %#lx.\n", GetLastError());
+    ok(prop_type == DEVPROP_TYPE_STRING, "got type %#lx\n", prop_type);
+    ok(size == sizeof(L"ROOT\\WINETEST\\0"), "got size %lu\n", size);
+    ok(!wcscmp(buffer_w, L"ROOT\\WINETEST\\0"), "got parent ID %s\n", debugstr_w(buffer_w));
+
+    /* DEVPKEY_Device_Children — set on the bus PDO after child enumeration.
+     * Open the bus device by its instance ID directly; the bus_class
+     * interface has been disabled earlier in this function, so a
+     * DIGCF_DEVICEINTERFACE-scoped enumeration would not find it.
+     *
+     * The test setup registers exactly one child ("Wine\Test\1"), so the
+     * full MULTI_SZ list is compared verbatim, including the trailing
+     * double-NUL terminator. */
+    {
+        static const WCHAR expected_children[] = L"Wine\\Test\\1\0";
+        SP_DEVINFO_DATA bus_dev = { sizeof(bus_dev) };
+        HDEVINFO bus_set;
+
+        bus_set = SetupDiCreateDeviceInfoList(NULL, NULL);
+        ok(bus_set != INVALID_HANDLE_VALUE, "failed to create bus device list, error %#lx\n",
+           GetLastError());
+        ret = SetupDiOpenDeviceInfoA(bus_set, "ROOT\\WINETEST\\0", NULL, 0, &bus_dev);
+        ok(ret, "failed to open bus device, error %#lx\n", GetLastError());
+
+        prop_type = DEVPROP_TYPE_EMPTY;
+        size = 0;
+        memset(buffer_w, 0, sizeof(buffer_w));
+        ret = SetupDiGetDevicePropertyW(bus_set, &bus_dev, &DEVPKEY_Device_Children,
+                                        &prop_type, (BYTE *)buffer_w, sizeof(buffer_w), &size, 0);
+        ok(ret, "DEVPKEY_Device_Children missing, error %#lx\n", GetLastError());
+        ok(prop_type == DEVPROP_TYPE_STRING_LIST, "got type %#lx\n", prop_type);
+        ok(size == sizeof(expected_children), "got size %lu, expected %Iu\n",
+           size, sizeof(expected_children));
+        ok(!memcmp(buffer_w, expected_children, size), "got children %s\n",
+           debugstr_w(buffer_w));
+
+        SetupDiDestroyDeviceInfoList(bus_set);
+    }
+
+    /* DEVPKEY_Device_Siblings — set on each child after bus enumeration.
+     * The test setup registers exactly one child, so the siblings list
+     * is empty. handle_bus_relations() writes the empty list as a single
+     * trailing NUL (one WCHAR). */
+    prop_type = DEVPROP_TYPE_EMPTY;
+    size = 0;
+    memset(buffer_w, 0, sizeof(buffer_w));
+    ret = SetupDiGetDevicePropertyW(set, &device, &DEVPKEY_Device_Siblings, &prop_type,
+                                    (BYTE *)buffer_w, sizeof(buffer_w), &size, 0);
+    ok(ret, "DEVPKEY_Device_Siblings missing, error %#lx\n", GetLastError());
+    ok(prop_type == DEVPROP_TYPE_STRING_LIST, "got type %#lx\n", prop_type);
+    ok(size == sizeof(WCHAR), "got size %lu, expected %Iu\n", size, sizeof(WCHAR));
+    ok(buffer_w[0] == 0, "got non-empty siblings %s\n", debugstr_w(buffer_w));
+
+    /* CM_Get_Parent on the PnP-managed child returns the bus parent. */
+    {
+        char parent_id[MAX_DEVICE_ID_LEN];
+        DEVINST cm_parent = 0;
+        CONFIGRET cm_ret;
+
+        cm_ret = CM_Get_Parent(&cm_parent, device.DevInst, 0);
+        ok(cm_ret == CR_SUCCESS, "CM_Get_Parent: got %#lx\n", cm_ret);
+        ok(cm_parent != 0, "got null parent devnode\n");
+
+        cm_ret = CM_Get_Device_IDA(cm_parent, parent_id, sizeof(parent_id), 0);
+        ok(cm_ret == CR_SUCCESS, "CM_Get_Device_IDA: got %#lx\n", cm_ret);
+        ok(!strcmp(parent_id, "ROOT\\WINETEST\\0"), "got parent ID %s\n", parent_id);
+    }
+
     ret = SetupDiEnumDeviceInterfaces(set, NULL, &child_class, 0, &iface);
     ok(ret, "failed to get interface, error %#lx\n", GetLastError());
     ok(IsEqualGUID(&iface.InterfaceClassGuid, &child_class),
diff --git a/dlls/odbc32/proxyodbc.c b/dlls/odbc32/proxyodbc.c
index 2ced00dc3fd1..dcd38eb1066c 100644
--- a/dlls/odbc32/proxyodbc.c
+++ b/dlls/odbc32/proxyodbc.c
@@ -1591,7 +1591,7 @@ static SQLRETURN alloc_con_handle( struct connection *con )
 static SQLRETURN create_con( struct connection *con )
 {
     WCHAR odbc_ver[6];
-    SQLSMALLINT len;
+    SQLSMALLINT len, i;
     SQLRETURN ret;
 
     if ((ret = alloc_con_handle( con ))) return ret;
@@ -1603,6 +1603,10 @@ static SQLRETURN create_con( struct connection *con )
             set_con_attr( con, SQL_ATTR_LOGIN_TIMEOUT, INT_PTR(con->attr_login_timeout), 0 ))
         WARN( "failed to set login timeout\n" );
 
+    for (i = 0; i < con->driver_attr_count; i++)
+        if ((ret = set_con_attr( con, con->driver_attr[i].id, con->driver_attr[i].value, con->driver_attr[i].len )))
+            WARN( "failed to set driver attribute %u error %u\n", con->driver_attr[i].id, ret );
+
     if (con->hdr.win32_handle)
     {
         ret = get_info_win32_w( con, SQL_DRIVER_ODBC_VER, odbc_ver, sizeof(odbc_ver), &len );
@@ -2470,6 +2474,7 @@ static void cleanup_object( struct object *obj )
     case SQL_HANDLE_ENV:
     {
         struct environment *env = (struct environment *)obj;
+
         RegCloseKey( env->drivers_key );
         RegCloseKey( env->sources_key );
         env->drivers_key = env->sources_key = NULL;
@@ -2479,6 +2484,12 @@ static void cleanup_object( struct object *obj )
     case SQL_HANDLE_DBC:
     {
         struct connection *con = (struct connection *)obj;
+        UINT32 i;
+
+        for (i = 0; i < con->driver_attr_count; i++) free( con->driver_attr[i].value );
+        free( con->driver_attr );
+        con->driver_attr = NULL;
+        con->driver_attr_count = 0;
         release_env( con->env );
         con->env = NULL;
         break;
@@ -2486,6 +2497,7 @@ static void cleanup_object( struct object *obj )
     case SQL_HANDLE_STMT:
     {
         struct statement *stmt = (struct statement *)obj;
+
         free_col_bindings( stmt );
         free_param_bindings( stmt );
         free_descriptors( stmt );
@@ -3877,6 +3889,27 @@ static SQLRETURN set_connect_attr_win32_a( struct connection *con, SQLINTEGER at
     return ret;
 }
 
+#ifndef SQL_CONNECT_OPT_DRVR_START
+#define SQL_CONNECT_OPT_DRVR_START  1000
+#endif
+
+static SQLRETURN store_driver_attribute( struct connection *con, SQLINTEGER id, SQLPOINTER value, SQLINTEGER len )
+{
+    struct driver_attribute *tmp, *attr;
+
+    if (!(tmp = realloc( con->driver_attr, (con->driver_attr_count + 1) * sizeof(*attr) ))) return SQL_ERROR;
+    con->driver_attr = tmp;
+
+    attr = &con->driver_attr[con->driver_attr_count];
+    attr->id = id;
+    if (!(attr->value = malloc( len ))) return SQL_ERROR;
+    memcpy( attr->value, value, len );
+    attr->len = len;
+
+    con->driver_attr_count++;
+    return SQL_SUCCESS;
+}
+
 /*************************************************************************
  *				SQLSetConnectAttr           [ODBC32.039]
  */
@@ -3914,8 +3947,13 @@ SQLRETURN WINAPI SQLSetConnectAttr(SQLHDBC ConnectionHandle, SQLINTEGER Attribut
             break;
 
         default:
-            FIXME( "unhandled attribute %d\n", Attribute );
-            ret = SQL_ERROR;
+            if (Attribute >= SQL_CONNECT_OPT_DRVR_START)
+                ret = store_driver_attribute( con, Attribute, Value, StringLength );
+            else
+            {
+                FIXME( "unhandled attribute %d\n", Attribute );
+                ret = SQL_ERROR;
+            }
             break;
         }
     }
@@ -7272,8 +7310,13 @@ SQLRETURN WINAPI SQLSetConnectAttrW(SQLHDBC ConnectionHandle, SQLINTEGER Attribu
             break;
 
         default:
-            FIXME( "unhandled attribute %d\n", Attribute );
-            ret = SQL_ERROR;
+            if (Attribute >= SQL_CONNECT_OPT_DRVR_START)
+                ret = store_driver_attribute( con, Attribute, Value, StringLength );
+            else
+            {
+                FIXME( "unhandled attribute %d\n", Attribute );
+                ret = SQL_ERROR;
+            }
             break;
         }
     }
diff --git a/dlls/odbc32/tests/odbc32.c b/dlls/odbc32/tests/odbc32.c
index b83e6ebfdc35..733222f128f9 100644
--- a/dlls/odbc32/tests/odbc32.c
+++ b/dlls/odbc32/tests/odbc32.c
@@ -73,6 +73,7 @@ DEFINE_EXPECT( driver_SQLGetInfo );
 DEFINE_EXPECT( driver_SQLGetInfo_SQL_DRIVER_ODBC_VER );
 DEFINE_EXPECT( driver_SQLConnect );
 DEFINE_EXPECT( driver_SQLGetConnectAttr );
+DEFINE_EXPECT( driver_SQLSetConnectAttr );
 DEFINE_EXPECT( driver_SQLDisconnect );
 DEFINE_EXPECT( driver_SQLDriverConnect );
 DEFINE_EXPECT( driver_SQLBrowseConnect );
@@ -176,8 +177,12 @@ static SQLRETURN WINAPI driver_SQLGetConnectAttr( SQLHDBC con, SQLINTEGER attr,
 static SQLRETURN WINAPI driver_SQLSetConnectAttr( SQLHDBC con, SQLINTEGER attr,
         SQLPOINTER val, SQLINTEGER len )
 {
-    ok( 0, "unexpected call\n" );
-    return SQL_ERROR;
+    CHECK_EXPECT( driver_SQLSetConnectAttr );
+    ok( attr == 1000, "attr = %d\n", attr );
+    ok( val != NULL, "val = %p\n", val );
+    ok( *(SQLUINTEGER *)val == 0xdeadbeef, "*val = %u\n", *(SQLUINTEGER *)val );
+    ok( len == sizeof(SQLUINTEGER), "len = %d\n", len );
+    return SQL_SUCCESS;
 }
 
 static SQLRETURN WINAPI driver_SQLGetInfo( SQLHDBC con, SQLUSMALLINT type,
@@ -730,7 +735,7 @@ static void test_SQLConnect( void )
     SQLHDBC con;
     SQLRETURN ret;
     SQLINTEGER size, version, pooling;
-    SQLUINTEGER timeout;
+    SQLUINTEGER timeout, driver_attr;
     SQLSMALLINT len;
     char str[32];
 
@@ -776,6 +781,10 @@ static void test_SQLConnect( void )
     ok( ret == SQL_SUCCESS, "got %d\n", ret );
     ok( timeout == SQL_LOGIN_TIMEOUT_DEFAULT, "wrong timeout %d\n", timeout );
 
+    driver_attr = 0xdeadbeef;
+    ret = SQLSetConnectAttr( con, 1000, &driver_attr, sizeof(driver_attr) );
+    ok( ret == SQL_SUCCESS, "got %d\n", ret );
+
     SET_EXPECT( driver_SQLAllocHandle_env );
     SET_EXPECT( driver_SQLSetEnvAttr );
     SET_EXPECT( driver_SQLAllocHandle_con );
@@ -783,6 +792,7 @@ static void test_SQLConnect( void )
     SET_EXPECT( driver_SQLConnect );
     SET_EXPECT( driver_SQLGetDiagRec );
     SET_EXPECT( driver_SQLGetInfo );
+    SET_EXPECT( driver_SQLSetConnectAttr );
     ret = SQLConnect( con, (SQLCHAR *)"winetest_dsn", SQL_NTS, (SQLCHAR *)"winetest", SQL_NTS, (SQLCHAR *)"winetest",
                       SQL_NTS );
     CHECK_CALLED( driver_SQLAllocHandle_env );
@@ -790,6 +800,7 @@ static void test_SQLConnect( void )
     CHECK_CALLED( driver_SQLAllocHandle_con );
     CHECK_CALLED( driver_SQLGetInfo_SQL_DRIVER_ODBC_VER );
     CHECK_CALLED( driver_SQLConnect );
+    CHECK_CALLED( driver_SQLSetConnectAttr);
     todo_wine CHECK_CALLED( driver_SQLGetDiagRec );
     todo_wine CHECK_CALLED( driver_SQLGetInfo );
     ok (ret == SQL_SUCCESS, "got %d\n", ret );
diff --git a/dlls/odbc32/unixlib.h b/dlls/odbc32/unixlib.h
index 48e34084274f..5a458e6f58a2 100644
--- a/dlls/odbc32/unixlib.h
+++ b/dlls/odbc32/unixlib.h
@@ -217,6 +217,13 @@ struct environment
     BOOL   sources_system;
 };
 
+struct driver_attribute
+{
+    UINT32 id;
+    void  *value;
+    UINT32 len;
+};
+
 struct connection
 {
     struct object hdr;
@@ -228,6 +235,9 @@ struct connection
     UINT32 attr_con_timeout;
     BOOL login_timeout_set;
     UINT32 attr_login_timeout;
+    /* driver attributes */
+    struct driver_attribute *driver_attr;
+    UINT32 driver_attr_count;
 };
 
 struct statement
diff --git a/dlls/ole32/clipboard.c b/dlls/ole32/clipboard.c
index c54608985fbd..749dd03c3e66 100644
--- a/dlls/ole32/clipboard.c
+++ b/dlls/ole32/clipboard.c
@@ -1899,6 +1899,9 @@ static HWND create_clipbrd_window(void);
  */
 static inline HRESULT get_clipbrd_window(ole_clipbrd *clipbrd, HWND *wnd)
 {
+    if ( clipbrd->window && !IsWindow(clipbrd->window) )
+        clipbrd->window = NULL;
+
     if ( !clipbrd->window )
         clipbrd->window = create_clipbrd_window();
 
diff --git a/dlls/ole32/dcom.idl b/dlls/ole32/dcom.idl
index 05c0513362f5..7004b8e80d0e 100644
--- a/dlls/ole32/dcom.idl
+++ b/dlls/ole32/dcom.idl
@@ -22,6 +22,7 @@
 #pragma makedep proxy
 #pragma makedep register
 
+#include "hstring.idl"
 #include "wine/orpc.idl"
 
 [
@@ -82,6 +83,61 @@ interface IRemUnknown2 : IRemUnknown
     [out, size_is(cIids)] MInterfacePointer **ppMIF);
 }
 
+[
+  object,
+  uuid(0000013C-0000-0000-C000-000000000046)
+]
+interface IRemUnknownN : IRemUnknown2
+{
+  const DWORD IRUF_CONVERTTOWEAK = 0x01;
+  const DWORD IRUF_CONVERTTOSTRONG = 0x02;
+  const DWORD IRUF_DISCONNECTIFLASTSTRONG = 0x04;
+
+  typedef struct tagXAptCallback
+  {
+    DWORD64 pfnCallback;
+    DWORD64 pParam;
+    DWORD64 pServerCtx;
+    DWORD64 pUnk;
+    GUID iid;
+    INT iMethod;
+    GUID guidProcessSecret;
+  } XAptCallback;
+
+  HRESULT AcknowledgeMarshalingSets(
+    [in] WORD cMarshalingSets,
+    [in, size_is(cMarshalingSets)] DWORD64 *pMarshalingSets);
+
+  HRESULT RemChangeRef(
+    [in] DWORD flags,
+    [in] WORD cInterfaceRefs,
+    [in, size_is(cInterfaceRefs)] REMINTERFACEREF InterfaceRefs[]);
+
+  HRESULT DoCallback(
+    [in] XAptCallback *pCallbackData);
+
+  HRESULT DoNonreentrantCallback(
+    [in] XAptCallback *pCallbackData);
+
+  HRESULT GetInterfaceNameFromIPID(
+    [in] REFIPID ripid,
+    [out] HSTRING *interfaceName);
+}
+
+[
+  object,
+  uuid(00000134-0000-0000-C000-000000000046),
+]
+interface IRundown : IRemUnknownN
+{
+  const DWORD MAX_OID_RUNDOWNS_PER_CALL = 100;
+
+  HRESULT RundownOid(
+    [in, range(1, MAX_OID_RUNDOWNS_PER_CALL)] DWORD cOid,
+    [in, size_is(cOid)] OID aOid[],
+    [out, size_is(cOid)] BYTE aRundownStatus[]);
+}
+
 [
   uuid(99fcfec4-5260-101b-bbcb-00aa0021347a),
   pointer_default(unique)
diff --git a/dlls/ole32/tests/clipboard.c b/dlls/ole32/tests/clipboard.c
index 50e4e8c27a39..6b10400af5aa 100644
--- a/dlls/ole32/tests/clipboard.c
+++ b/dlls/ole32/tests/clipboard.c
@@ -1925,6 +1925,96 @@ static void test_get_clipboard_locked(void)
     OleUninitialize();
 }
 
+struct sta_clipboard_thread_data
+{
+    const char *text;
+    HRESULT hr_set;
+};
+
+static DWORD CALLBACK sta_clipboard_set_thread(void *arg)
+{
+    struct sta_clipboard_thread_data *data = arg;
+    IDataObject *dataobj;
+    HRESULT hr;
+
+    hr = OleInitialize(NULL);
+    if (FAILED(hr))
+    {
+        data->hr_set = hr;
+        return 1;
+    }
+
+    hr = DataObjectImpl_CreateText(data->text, &dataobj);
+    ok(hr == S_OK, "DataObjectImpl_CreateText returned %#lx\n", hr);
+
+    hr = OleSetClipboard(dataobj);
+    data->hr_set = hr;
+    IDataObject_Release(dataobj);
+
+    if (SUCCEEDED(hr))
+        OleFlushClipboard();
+
+    OleUninitialize();
+    return 0;
+}
+
+static void test_sta_clipboard_reuse(void)
+{
+    struct sta_clipboard_thread_data data;
+    HANDLE thread, hdata;
+    HRESULT hr;
+    char *text;
+    DWORD ret;
+
+    hr = OleInitialize(NULL);
+    ok(hr == S_OK, "OleInitialize returned %#lx\n", hr);
+
+    /* First STA thread sets clipboard to "first". */
+    data.text = "first";
+    data.hr_set = E_FAIL;
+    thread = CreateThread(NULL, 0, sta_clipboard_set_thread, &data, 0, NULL);
+    ok(thread != NULL, "CreateThread failed (%ld)\n", GetLastError());
+    ret = WaitForSingleObject(thread, 5000);
+    ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", ret);
+    CloseHandle(thread);
+    ok(data.hr_set == S_OK, "first OleSetClipboard returned %#lx\n", data.hr_set);
+
+    ok(OpenClipboard(NULL), "OpenClipboard failed\n");
+    hdata = GetClipboardData(CF_TEXT);
+    ok(hdata != NULL, "GetClipboardData returned NULL\n");
+    if (hdata)
+    {
+        text = GlobalLock(hdata);
+        ok(!strcmp(text, "first"), "expected \"first\", got \"%s\"\n", text);
+        GlobalUnlock(hdata);
+    }
+    CloseClipboard();
+
+    /* Regression test: second STA thread should be able to set the
+       clipboard after the first thread's clipboard window is destroyed. */
+    data.text = "second";
+    data.hr_set = E_FAIL;
+    thread = CreateThread(NULL, 0, sta_clipboard_set_thread, &data, 0, NULL);
+    ok(thread != NULL, "CreateThread failed (%ld)\n", GetLastError());
+    ret = WaitForSingleObject(thread, 5000);
+    ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", ret);
+    CloseHandle(thread);
+    ok(data.hr_set == S_OK, "second OleSetClipboard returned %#lx\n", data.hr_set);
+
+    ok(OpenClipboard(NULL), "OpenClipboard failed\n");
+    hdata = GetClipboardData(CF_TEXT);
+    ok(hdata != NULL, "GetClipboardData returned NULL\n");
+    if (hdata)
+    {
+        text = GlobalLock(hdata);
+        ok(!strcmp(text, "second"), "expected \"second\", got \"%s\"\n", text);
+        GlobalUnlock(hdata);
+    }
+    CloseClipboard();
+
+    OleUninitialize();
+}
+
 START_TEST(clipboard)
 {
     test_get_clipboard_uninitialized();
@@ -1935,5 +2025,6 @@ START_TEST(clipboard)
     test_nonole_clipboard();
     test_getdatahere();
     test_multithreaded_clipboard();
+    test_sta_clipboard_reuse();
     test_get_clipboard_locked();
 }
diff --git a/dlls/ole32/tests/compobj.c b/dlls/ole32/tests/compobj.c
index 2c4cc566f3aa..b4e12b38fe11 100644
--- a/dlls/ole32/tests/compobj.c
+++ b/dlls/ole32/tests/compobj.c
@@ -32,6 +32,7 @@
 #include "urlmon.h" /* for CLSID_FileProtocol */
 #include "dde.h"
 #include "cguid.h"
+#include "comsvcs.h"
 
 #include "ctxtcall.h"
 
@@ -69,6 +70,7 @@ DEFINE_EXPECT(PreInitialize);
 DEFINE_EXPECT(PostInitialize);
 DEFINE_EXPECT(PreUninitialize);
 DEFINE_EXPECT(PostUninitialize);
+DEFINE_EXPECT(context_callback_func);
 
 /* functions that are not present on all versions of Windows */
 static HRESULT (WINAPI * pCoGetObjectContext)(REFIID riid, LPVOID *ppv);
@@ -2000,15 +2002,98 @@ static void test_CoFreeUnusedLibraries(void)
     CoUninitialize();
 }
 
+struct context_callback_arg
+{
+    IContextCallback *context_callback;
+    ULONG_PTR token;
+    BOOL is_mta;
+    GUID logical_thread_id;
+    BOOL todo_thread_id;
+};
+
+static HRESULT WINAPI context_callback_func(ComCallData *arg)
+{
+    struct context_callback_arg *ctx = (struct context_callback_arg *)arg;
+    ULONG_PTR token;
+    GUID thread_id;
+    HRESULT hr;
+
+    CHECK_EXPECT(context_callback_func);
+
+    hr = CoGetCurrentLogicalThreadId(&thread_id);
+    ok_ole_success(hr, "CoGetCurrentLgoicalThreadId");
+    todo_wine_if(ctx->todo_thread_id) ok(IsEqualIID(&thread_id, &ctx->logical_thread_id),
+            "thread_id = %s\n", wine_dbgstr_guid(&thread_id));
+
+    hr = pCoGetContextToken(&token);
+    ok_ole_success(hr, "CoGetContextToken");
+    ok(token == ctx->token, "executed in different context\n");
+    return S_FALSE;
+}
+
+static DWORD WINAPI context_callback_thread(void *arg)
+{
+    struct context_callback_arg *ctx = arg;
+    HRESULT hr;
+    GUID id;
+
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    hr = CoGetCurrentLogicalThreadId(&id);
+    ok_ole_success(hr, "CoGetCurrentLogicalThreadId");
+    hr = IContextCallback_QueryInterface(ctx->context_callback, &IID_IObjContext, (void **)&ctx->token);
+    ok_ole_success(hr, "IContextCallback_QueryInterface");
+    IObjContext_Release((IObjContext *)ctx->token);
+
+    SET_EXPECT(context_callback_func);
+    ctx->logical_thread_id = id;
+    /* TODO: native calls the callback in current thread after temporarily converting it to MTA */
+    if (ctx->is_mta) ctx->todo_thread_id = TRUE;
+    hr = IContextCallback_ContextCallback(ctx->context_callback, context_callback_func,
+            (ComCallData *)ctx, &IID_IContextCallback, 2, NULL);
+    ctx->todo_thread_id = FALSE;
+    CHECK_CALLED(context_callback_func, 1);
+    ok(hr == S_FALSE, "got 0x%08lx\n", hr);
+
+    SET_EXPECT(context_callback_func);
+    ctx->logical_thread_id = IID_IEnterActivityWithNoLock;
+    hr = IContextCallback_ContextCallback(ctx->context_callback, context_callback_func,
+            (ComCallData *)ctx, &IID_IEnterActivityWithNoLock, 2, NULL);
+    CHECK_CALLED(context_callback_func, 1);
+    ok(hr == S_FALSE, "got 0x%08lx\n", hr);
+
+    CoUninitialize();
+
+    CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+    SET_EXPECT(context_callback_func);
+    ctx->logical_thread_id = id;
+    hr = IContextCallback_ContextCallback(ctx->context_callback, context_callback_func,
+            (ComCallData *)ctx, &IID_IContextCallback, 2, NULL);
+    CHECK_CALLED(context_callback_func, 1);
+    ok(hr == S_FALSE, "got 0x%08lx\n", hr);
+
+    SET_EXPECT(context_callback_func);
+    ctx->logical_thread_id = IID_IEnterActivityWithNoLock;
+    hr = IContextCallback_ContextCallback(ctx->context_callback, context_callback_func,
+            (ComCallData *)ctx, &IID_IEnterActivityWithNoLock, 2, NULL);
+    CHECK_CALLED(context_callback_func, 1);
+    ok(hr == S_FALSE, "got 0x%08lx\n", hr);
+
+    CoUninitialize();
+    return 0;
+}
+
 static void test_CoGetObjectContext(void)
 {
     HRESULT hr;
     ULONG refs;
     IComThreadingInfo *pComThreadingInfo, *threadinginfo2;
+    struct context_callback_arg callback_arg;
     IContextCallback *pContextCallback;
-    IObjContext *pObjContext;
     APTTYPE apttype;
     THDTYPE thdtype;
+    HANDLE thread;
     GUID id, id2;
 
     if (!pCoGetObjectContext)
@@ -2044,6 +2129,21 @@ static void test_CoGetObjectContext(void)
     hr = CoGetCurrentLogicalThreadId(&id2);
     ok(IsEqualGUID(&id, &id2), "got %s, expected %s\n", wine_dbgstr_guid(&id), wine_dbgstr_guid(&id2));
 
+    id = GUID_NULL;
+    hr = IComThreadingInfo_SetCurrentLogicalThreadId(pComThreadingInfo, &id);
+    ok(hr == S_OK, "got 0x%08lx\n", hr);
+
+    hr = IComThreadingInfo_GetCurrentLogicalThreadId(pComThreadingInfo, &id);
+    ok(hr == S_OK, "got 0x%08lx\n", hr);
+    ok(IsEqualGUID(&id, &GUID_NULL), "id = %s\n", wine_dbgstr_guid(&id));
+
+    hr = CoGetCurrentLogicalThreadId(&id);
+    ok(hr == S_OK, "got 0x%08lx\n", hr);
+    ok(IsEqualGUID(&id, &GUID_NULL), "id = %s\n", wine_dbgstr_guid(&id));
+
+    hr = IComThreadingInfo_SetCurrentLogicalThreadId(pComThreadingInfo, &id2);
+    ok(hr == S_OK, "got 0x%08lx\n", hr);
+
     hr = IComThreadingInfo_GetCurrentApartmentType(pComThreadingInfo, &apttype);
     ok_ole_success(hr, "IComThreadingInfo_GetCurrentApartmentType");
     ok(apttype == APTTYPE_MAINSTA, "apartment type should be APTTYPE_MAINSTA instead of %d\n", apttype);
@@ -2058,6 +2158,50 @@ static void test_CoGetObjectContext(void)
     hr = pCoGetObjectContext(&IID_IContextCallback, (void **)&pContextCallback);
     ok_ole_success(hr, "CoGetObjectContext(ContextCallback)");
 
+    callback_arg.context_callback = pContextCallback;
+    hr = pCoGetObjectContext(&IID_IObjContext, (void **)&callback_arg.token);
+    ok_ole_success(hr, "CoGetObjectContext");
+    IObjContext_Release((IObjContext *)callback_arg.token);
+    callback_arg.is_mta = FALSE;
+    callback_arg.logical_thread_id = id2;
+    callback_arg.todo_thread_id = FALSE;
+
+    SET_EXPECT(context_callback_func);
+    hr = IContextCallback_ContextCallback(pContextCallback, context_callback_func,
+            (ComCallData *)&callback_arg, &IID_IContextCallback, 2, NULL);
+    CHECK_CALLED(context_callback_func, 1);
+    ok(hr == S_FALSE, "got 0x%08lx\n", hr);
+
+    SET_EXPECT(context_callback_func);
+    callback_arg.logical_thread_id = IID_IEnterActivityWithNoLock;
+    hr = IContextCallback_ContextCallback(pContextCallback, context_callback_func,
+            (ComCallData *)&callback_arg, &IID_IEnterActivityWithNoLock, 2, NULL);
+    CHECK_CALLED(context_callback_func, 1);
+    ok(hr == S_FALSE, "got 0x%08lx\n", hr);
+
+    thread = CreateThread(NULL, 0, context_callback_thread, &callback_arg, 0, NULL);
+    ok(thread != NULL, "CreateThread failed\n");
+    while (1)
+    {
+        MSG msg;
+
+        switch(MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLPOSTMESSAGE))
+        {
+        case WAIT_OBJECT_0:
+            break;
+        case WAIT_OBJECT_0 + 1:
+            while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
+                DispatchMessageA(&msg);
+            continue;
+        default:
+            ok(0, "MsgWaitForMultipleObjects failed\n");
+            break;
+        }
+
+        break;
+    }
+    CloseHandle(thread);
+
     refs = IContextCallback_Release(pContextCallback);
     ok(refs == 0, "pContextCallback should have 0 refs instead of %ld refs\n", refs);
 
@@ -2082,14 +2226,34 @@ static void test_CoGetObjectContext(void)
     hr = pCoGetObjectContext(&IID_IContextCallback, (void **)&pContextCallback);
     ok_ole_success(hr, "CoGetObjectContext(ContextCallback)");
 
-    refs = IContextCallback_Release(pContextCallback);
-    ok(refs == 0, "pContextCallback should have 0 refs instead of %ld refs\n", refs);
-
-    hr = pCoGetObjectContext(&IID_IObjContext, (void **)&pObjContext);
+    callback_arg.context_callback = pContextCallback;
+    hr = pCoGetObjectContext(&IID_IObjContext, (void **)&callback_arg.token);
     ok_ole_success(hr, "CoGetObjectContext");
+    IObjContext_Release((IObjContext *)callback_arg.token);
+    callback_arg.is_mta = TRUE;
+    callback_arg.logical_thread_id = id2;
+    callback_arg.todo_thread_id = FALSE;
+
+    SET_EXPECT(context_callback_func);
+    hr = IContextCallback_ContextCallback(pContextCallback, context_callback_func,
+            (ComCallData *)&callback_arg, &IID_IContextCallback, 2, NULL);
+    CHECK_CALLED(context_callback_func, 1);
+    ok(hr == S_FALSE, "got 0x%08lx\n", hr);
+
+    SET_EXPECT(context_callback_func);
+    callback_arg.logical_thread_id = IID_IEnterActivityWithNoLock;
+    hr = IContextCallback_ContextCallback(pContextCallback, context_callback_func,
+            (ComCallData *)&callback_arg, &IID_IEnterActivityWithNoLock, 2, NULL);
+    CHECK_CALLED(context_callback_func, 1);
+    ok(hr == S_FALSE, "got 0x%08lx\n", hr);
+
+    thread = CreateThread(NULL, 0, context_callback_thread, &callback_arg, 0, NULL);
+    ok(thread != NULL, "CreateThread failed\n");
+    WaitForSingleObject(thread, INFINITE);
+    CloseHandle(thread);
 
-    refs = IObjContext_Release(pObjContext);
-    ok(refs == 0, "pObjContext should have 0 refs instead of %ld refs\n", refs);
+    refs = IContextCallback_Release(pContextCallback);
+    ok(refs == 0, "pContextCallback should have 0 refs instead of %ld refs\n", refs);
 
     CoUninitialize();
 }
@@ -2201,12 +2365,42 @@ static void test_CoGetCallContext(void)
     CoUninitialize();
 }
 
+static DWORD WINAPI get_context_token_thread(void *arg)
+{
+    ULONG_PTR mta_token = (ULONG_PTR)arg, token;
+    HRESULT hr;
+    ULONG refs;
+
+    test_apt_type(APTTYPE_MTA, APTTYPEQUALIFIER_IMPLICIT_MTA);
+    hr = pCoGetContextToken(&token);
+    ok(hr == S_OK, "Expected S_OK, got 0x%08lx\n", hr);
+    ok(token, "Expected token != 0\n");
+    ok(token == mta_token, "token != mta_token\n");
+
+    refs = IUnknown_AddRef((IUnknown *)token);
+    ok(refs == 1, "Expected 1, got %lu\n", refs);
+    IUnknown_Release((IUnknown *)token);
+
+    hr = CoInitialize(NULL);
+    ok(hr == S_OK, "CoInitialize() failed with error 0x%08lx\n", hr);
+    test_apt_type(APTTYPE_MAINSTA, APTTYPEQUALIFIER_NONE);
+
+    hr = pCoGetContextToken(&token);
+    ok(hr == S_OK, "Expected S_OK, got 0x%08lx\n", hr);
+    ok(token, "Expected token != 0\n");
+    ok(token != mta_token, "token == mta_token\n");
+
+    CoUninitialize();
+    return 0;
+}
+
 static void test_CoGetContextToken(void)
 {
     HRESULT hr;
     ULONG refs;
     ULONG_PTR token, token2;
     IObjContext *ctx;
+    HANDLE thread;
 
     if (!pCoGetContextToken)
     {
@@ -2267,6 +2461,22 @@ static void test_CoGetContextToken(void)
     refs = IObjContext_Release(ctx);
     ok(refs == 1, "Expected 0, got %lu\n", refs);
 
+    CoUninitialize();
+
+    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+    ok(hr == S_OK, "Expected S_OK, got 0x%08lx\n", hr);
+
+    token = 0;
+    hr = pCoGetContextToken(&token);
+    ok(hr == S_OK, "Expected S_OK, got 0x%08lx\n", hr);
+    ok(token, "Expected token != 0\n");
+    ok(token != token2, "token did not change\n");
+
+    thread = CreateThread(NULL, 0, get_context_token_thread, (void*)token, 0, NULL);
+    ok(thread != NULL, "CreateThread failed\n");
+    WaitForSingleObject(thread, INFINITE);
+    CloseHandle(thread);
+
     refs = IObjContext_Release(ctx);
     ok(refs == 0, "Expected 0, got %lu\n", refs);
 
diff --git a/dlls/oleaut32/olepropframe.c b/dlls/oleaut32/olepropframe.c
index cad1a2ad5c2e..3106ebd6b544 100644
--- a/dlls/oleaut32/olepropframe.c
+++ b/dlls/oleaut32/olepropframe.c
@@ -286,6 +286,8 @@ HRESULT WINAPI OleCreatePropertyFrameIndirect(LPOCPFIPARAMS lpParams)
         if(FAILED(res))
             WARN("SetObjects() failed, hr %#lx.\n", res);
 
+        memset(&page_info, 0, sizeof(page_info));
+        page_info.cb = sizeof(page_info);
         res = IPropertyPage_GetPageInfo(property_page[i], &page_info);
         if(FAILED(res))
             continue;
diff --git a/dlls/oleaut32/tests/olepicture.c b/dlls/oleaut32/tests/olepicture.c
index 830687cb6989..614020a1026c 100644
--- a/dlls/oleaut32/tests/olepicture.c
+++ b/dlls/oleaut32/tests/olepicture.c
@@ -1770,7 +1770,7 @@ static void test_load_save_emf(void)
     ok(size == -1, "expected -1, got %ld\n", size);
 
     offset.QuadPart = 0;
-    hr = IStream_Seek(dst_stream, offset, SEEK_SET, NULL);
+    hr = IStream_Seek(dst_stream, offset, STREAM_SEEK_SET, NULL);
     ok(hr == S_OK, "IStream_Seek %#lx\n", hr);
 
     hr = IPicture_QueryInterface(pic, &IID_IPersistStream, (void **)&src_stream);
diff --git a/dlls/opengl32/tests/opengl.c b/dlls/opengl32/tests/opengl.c
index c9429d3d8514..ed28698c2817 100644
--- a/dlls/opengl32/tests/opengl.c
+++ b/dlls/opengl32/tests/opengl.c
@@ -3658,8 +3658,8 @@ static void test_framebuffer(void)
         ok_ret( TRUE, wglDeleteContext( ctx2 ) );
         ctx2 = ext.wglCreateContextAttribsARB( hdc2, ctx, NULL );
         ok_ptr( ctx2, !=, NULL );
-        ok_ret( FALSE, ext.wglMakeContextCurrentARB( hdc, hdc2, ctx2 ) );
-        ok_ret( ERROR_INVALID_PIXEL_FORMAT, LOWORD( GetLastError() ) );
+        todo_wine ok_ret( FALSE, ext.wglMakeContextCurrentARB( hdc, hdc2, ctx2 ) );
+        todo_wine ok_ret( ERROR_INVALID_PIXEL_FORMAT, LOWORD( GetLastError() ) );
     }
 
     ok_ret( TRUE, ext.wglMakeContextCurrentARB( hdc2, hdc2, ctx2 ) );
diff --git a/dlls/packager/Makefile.in b/dlls/packager/Makefile.in
index 3589b37be9c4..5fc89d807a81 100644
--- a/dlls/packager/Makefile.in
+++ b/dlls/packager/Makefile.in
@@ -1,6 +1,5 @@
 MODULE = packager.dll
 IMPORTS = uuid shell32 shlwapi user32
-EXTRADLLFLAGS = -Wb,--prefer-native
 
 SOURCES = \
 	packager.rc \
diff --git a/dlls/pdh/Makefile.in b/dlls/pdh/Makefile.in
index b8616aaaa266..5738f0e743b3 100644
--- a/dlls/pdh/Makefile.in
+++ b/dlls/pdh/Makefile.in
@@ -1,7 +1,5 @@
 MODULE    = pdh.dll
 IMPORTLIB = pdh
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	pdh_main.c
diff --git a/dlls/qasf/tests/dmowrapper.c b/dlls/qasf/tests/dmowrapper.c
index b8d033d7a8f9..6aaf3969645e 100644
--- a/dlls/qasf/tests/dmowrapper.c
+++ b/dlls/qasf/tests/dmowrapper.c
@@ -324,27 +324,27 @@ static HRESULT WINAPI dmo_ProcessInput(IMediaObject *iface, DWORD index,
     if (testmode == 0 || testmode == 12 || testmode == 100)
     {
         ok(!flags, "Got flags %#lx.\n", flags);
-        ok(!timestamp, "Got timestamp %s.\n", wine_dbgstr_longlong(timestamp));
-        ok(!timelength, "Got length %s.\n", wine_dbgstr_longlong(timelength));
+        ok(!timestamp, "Got timestamp %I64d.\n", timestamp);
+        ok(!timelength, "Got length %I64d.\n", timelength);
     }
     else if (testmode == 1)
     {
         ok(flags == (DMO_INPUT_DATA_BUFFERF_TIME | DMO_INPUT_DATA_BUFFERF_TIMELENGTH), "Got flags %#lx.\n", flags);
-        ok(timestamp == 20000, "Got timestamp %s.\n", wine_dbgstr_longlong(timestamp));
-        ok(timelength == 1, "Got length %s.\n", wine_dbgstr_longlong(timelength));
+        ok(timestamp == 20000, "Got timestamp %I64d.\n", timestamp);
+        ok(timelength == 1, "Got length %I64d.\n", timelength);
     }
     else if (testmode == 6)
     {
         ok(flags == (DMO_INPUT_DATA_BUFFERF_TIME | DMO_INPUT_DATA_BUFFERF_TIMELENGTH
                 | DMO_INPUT_DATA_BUFFERF_SYNCPOINT), "Got flags %#lx.\n", flags);
-        ok(timestamp == 20000, "Got timestamp %s.\n", wine_dbgstr_longlong(timestamp));
-        ok(timelength == 10000, "Got length %s.\n", wine_dbgstr_longlong(timelength));
+        ok(timestamp == 20000, "Got timestamp %I64d.\n", timestamp);
+        ok(timelength == 10000, "Got length %I64d.\n", timelength);
     }
     else
     {
         ok(flags == (DMO_INPUT_DATA_BUFFERF_TIME | DMO_INPUT_DATA_BUFFERF_TIMELENGTH), "Got flags %#lx.\n", flags);
-        ok(timestamp == 20000, "Got timestamp %s.\n", wine_dbgstr_longlong(timestamp));
-        ok(timelength == 10000, "Got length %s.\n", wine_dbgstr_longlong(timelength));
+        ok(timestamp == 20000, "Got timestamp %I64d.\n", timestamp);
+        ok(timelength == 10000, "Got length %I64d.\n", timelength);
     }
 
     testdmo_buffer = buffer;
@@ -1259,15 +1259,15 @@ static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample
     if (testmode == 8)
     {
         ok(hr == VFW_S_NO_STOP_TIME, "Got hr %#lx.\n", hr);
-        ok(start == 5000, "Got start time %s.\n", wine_dbgstr_longlong(start));
+        ok(start == 5000, "Got start time %I64d.\n", start);
     }
     else if (testmode == 9)
         ok(hr == VFW_E_SAMPLE_TIME_NOT_SET, "Got hr %#lx.\n", hr);
     else
     {
         ok(hr == S_OK, "Got hr %#lx.\n", hr);
-        ok(start == 5000, "Got start time %s.\n", wine_dbgstr_longlong(start));
-        ok(stop == 6000, "Got stop time %s.\n", wine_dbgstr_longlong(stop));
+        ok(start == 5000, "Got start time %I64d.\n", start);
+        ok(stop == 6000, "Got stop time %I64d.\n", stop);
     }
 
     hr = IMediaSample_GetMediaTime(sample, &start, &stop);
@@ -1307,8 +1307,8 @@ static HRESULT testsink_new_segment(struct strmbase_sink *iface,
 {
     struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter);
     ++filter->got_new_segment;
-    ok(start == 10000, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(stop == 20000, "Got stop %s.\n", wine_dbgstr_longlong(stop));
+    ok(start == 10000, "Got start %I64d.\n", start);
+    ok(stop == 20000, "Got stop %I64d.\n", stop);
     ok(rate == 1.0, "Got rate %.16e.\n", rate);
     return 0xdeadbeef;
 }
diff --git a/dlls/qcap/qcap_classes.idl b/dlls/qcap/qcap_classes.idl
index c1ec48bc3524..39812ad713c1 100644
--- a/dlls/qcap/qcap_classes.idl
+++ b/dlls/qcap/qcap_classes.idl
@@ -28,7 +28,7 @@
 coclass AudioRecord {}
 
 [
-    helpstring("Audio Capture Filter"),
+    helpstring("AVI Compressor"),
     threading(both),
     uuid(d76e2820-1563-11cf-ac98-00aa004c0fa9)
 ]
diff --git a/dlls/qcap/tests/avimux.c b/dlls/qcap/tests/avimux.c
index 8d11c5cbf040..5fdc3caa244d 100644
--- a/dlls/qcap/tests/avimux.c
+++ b/dlls/qcap/tests/avimux.c
@@ -783,12 +783,12 @@ static void test_seeking(void)
     hr = IMediaSeeking_GetDuration(seeking, &time);
     todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr);
     if (hr == S_OK)
-        ok(!time, "Got duration %s.\n", wine_dbgstr_longlong(time));
+        ok(!time, "Got duration %I64d.\n", time);
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr);
     if (hr == S_OK)
-        ok(!time, "Got duration %s.\n", wine_dbgstr_longlong(time));
+        ok(!time, "Got duration %I64d.\n", time);
 
     hr = IMediaSeeking_GetStopPosition(seeking, &time);
     ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr);
diff --git a/dlls/qcap/tests/smartteefilter.c b/dlls/qcap/tests/smartteefilter.c
index a25889d2a4d4..bae2a03d5a23 100644
--- a/dlls/qcap/tests/smartteefilter.c
+++ b/dlls/qcap/tests/smartteefilter.c
@@ -803,8 +803,8 @@ static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample
     else
     {
         ok(hr == S_OK, "Got hr %#lx.\n", hr);
-        ok(start == 30000, "Got start time %s.\n", wine_dbgstr_longlong(start));
-        ok(stop == 40000, "Got stop time %s.\n", wine_dbgstr_longlong(stop));
+        ok(start == 30000, "Got start time %I64d.\n", start);
+        ok(stop == 40000, "Got stop time %I64d.\n", stop);
     }
 
     hr = IMediaSample_GetMediaTime(sample, &start, &stop);
@@ -815,8 +815,8 @@ static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample
     else
     {
         ok(hr == S_OK, "Got hr %#lx.\n", hr);
-        ok(start == 10000, "Got start time %s.\n", wine_dbgstr_longlong(start));
-        ok(stop == 20000, "Got stop time %s.\n", wine_dbgstr_longlong(stop));
+        ok(start == 10000, "Got start time %I64d.\n", start);
+        ok(stop == 20000, "Got stop time %I64d.\n", stop);
     }
 
     hr = IMediaSample_IsDiscontinuity(sample);
@@ -835,8 +835,8 @@ static HRESULT testsink_new_segment(struct strmbase_sink *iface,
         REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
 {
     struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter);
-    ok(start == 10000, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(stop == 20000, "Got stop %s.\n", wine_dbgstr_longlong(stop));
+    ok(start == 10000, "Got start %I64d.\n", start);
+    ok(stop == 20000, "Got stop %I64d.\n", stop);
     ok(rate == 1.0, "Got rate %.16e.\n", rate);
     SetEvent(filter->segment_event);
     return S_OK;
diff --git a/dlls/qdvd/Makefile.in b/dlls/qdvd/Makefile.in
index 649b11f12d9d..8de5f927cdea 100644
--- a/dlls/qdvd/Makefile.in
+++ b/dlls/qdvd/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = qdvd.dll
 IMPORTS   = strmbase strmiids uuid ole32 msvcrt
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_PRODUCTVERSION = 6,5,1,904
 
 SOURCES = \
diff --git a/dlls/qedit/tests/samplegrabber.c b/dlls/qedit/tests/samplegrabber.c
index 9daa67355b2f..f12326ca8448 100644
--- a/dlls/qedit/tests/samplegrabber.c
+++ b/dlls/qedit/tests/samplegrabber.c
@@ -752,13 +752,13 @@ static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample
 
     hr = IMediaSample_GetTime(sample, &start, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start == 30000, "Got start time %s.\n", wine_dbgstr_longlong(start));
-    ok(stop == 40000, "Got stop time %s.\n", wine_dbgstr_longlong(stop));
+    ok(start == 30000, "Got start time %I64d.\n", start);
+    ok(stop == 40000, "Got stop time %I64d.\n", stop);
 
     hr = IMediaSample_GetMediaTime(sample, &start, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start == 10000, "Got start time %s.\n", wine_dbgstr_longlong(start));
-    ok(stop == 20000, "Got stop time %s.\n", wine_dbgstr_longlong(stop));
+    ok(start == 10000, "Got start time %I64d.\n", start);
+    ok(stop == 20000, "Got stop time %I64d.\n", stop);
 
     hr = IMediaSample_IsDiscontinuity(sample);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -777,8 +777,8 @@ static HRESULT testsink_new_segment(struct strmbase_sink *iface,
 {
     struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter);
     ++filter->got_new_segment;
-    ok(start == 10000, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(stop == 20000, "Got stop %s.\n", wine_dbgstr_longlong(stop));
+    ok(start == 10000, "Got start %I64d.\n", start);
+    ok(stop == 20000, "Got stop %I64d.\n", stop);
     ok(rate == 1.0, "Got rate %.16e.\n", rate);
     return S_OK;
 }
diff --git a/dlls/quartz/filesource.c b/dlls/quartz/filesource.c
index 2f85808d43e2..02cae5b654bc 100644
--- a/dlls/quartz/filesource.c
+++ b/dlls/quartz/filesource.c
@@ -872,8 +872,7 @@ static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader *iface,
     HRESULT hr;
     BOOL ret;
 
-    TRACE("filter %p, offset %s, length %ld, buffer %p.\n",
-            filter, wine_dbgstr_longlong(offset), length, buffer);
+    TRACE("filter %p, offset %#I64x, length %ld, buffer %p.\n", filter, offset, length, buffer);
 
     ret = sync_read(filter->file, offset, length, buffer, &read_len);
     if (ret)
diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c
index 79fcc425e535..efe604530dbb 100644
--- a/dlls/quartz/filtergraph.c
+++ b/dlls/quartz/filtergraph.c
@@ -2320,8 +2320,7 @@ static HRESULT WINAPI MediaSeeking_GetDuration(IMediaSeeking *iface, LONGLONG *d
 
     LeaveCriticalSection(&graph->cs);
 
-    TRACE("Returning hr %#lx, duration %s (%s seconds).\n", hr,
-            wine_dbgstr_longlong(*duration), debugstr_time(*duration));
+    TRACE("Returning hr %#lx, duration %I64d (%s seconds).\n", hr, *duration, debugstr_time(*duration));
     return hr;
 }
 
@@ -2362,7 +2361,7 @@ static HRESULT WINAPI MediaSeeking_GetStopPosition(IMediaSeeking *iface, LONGLON
 
     LeaveCriticalSection(&graph->cs);
 
-    TRACE("Returning %s (%s seconds).\n", wine_dbgstr_longlong(*stop), debugstr_time(*stop));
+    TRACE("Returning %I64d (%s seconds).\n", *stop, debugstr_time(*stop));
     return hr;
 }
 
@@ -2392,7 +2391,7 @@ static HRESULT WINAPI MediaSeeking_GetCurrentPosition(IMediaSeeking *iface, LONG
 
     LeaveCriticalSection(&graph->cs);
 
-    TRACE("Returning %s (%s seconds).\n", wine_dbgstr_longlong(ret), debugstr_time(ret));
+    TRACE("Returning %I64d (%s seconds).\n", ret, debugstr_time(ret));
     *current = ret;
 
     return S_OK;
@@ -2403,8 +2402,8 @@ static HRESULT WINAPI MediaSeeking_ConvertTimeFormat(IMediaSeeking *iface, LONGL
 {
     struct filter_graph *This = impl_from_IMediaSeeking(iface);
 
-    TRACE("(%p/%p)->(%p, %s, 0x%s, %s)\n", This, iface, pTarget,
-        debugstr_guid(pTargetFormat), wine_dbgstr_longlong(Source), debugstr_guid(pSourceFormat));
+    TRACE("graph %p, target %p, target_format %s, source %I64d, source_format %s.\n",
+            This, pTarget, debugstr_guid(pTargetFormat), Source, debugstr_guid(pSourceFormat));
 
     if (!pSourceFormat)
         pSourceFormat = &This->timeformatseek;
@@ -2428,15 +2427,12 @@ static HRESULT WINAPI MediaSeeking_SetPositions(IMediaSeeking *iface, LONGLONG *
     struct filter *filter;
     FILTER_STATE state;
 
-    TRACE("graph %p, current %s, current_flags %#lx, stop %s, stop_flags %#lx.\n", graph,
-            current_ptr ? wine_dbgstr_longlong(*current_ptr) : "<null>", current_flags,
-            stop_ptr ? wine_dbgstr_longlong(*stop_ptr): "<null>", stop_flags);
+    TRACE("graph %p, current %p, current_flags %#lx, stop %p, stop_flags %#lx.\n",
+            graph, current_ptr, current_flags, stop_ptr, stop_flags);
     if (current_ptr)
-        TRACE("Setting current position to %s (%s seconds).\n",
-                wine_dbgstr_longlong(*current_ptr), debugstr_time(*current_ptr));
+        TRACE("Setting current position to %I64d (%s seconds).\n", *current_ptr, debugstr_time(*current_ptr));
     if (stop_ptr)
-        TRACE("Setting stop position to %s (%s seconds).\n",
-                wine_dbgstr_longlong(*stop_ptr), debugstr_time(*stop_ptr));
+        TRACE("Setting stop position to %I64d (%s seconds).\n", *stop_ptr, debugstr_time(*stop_ptr));
 
     if ((current_flags & 0x7) != AM_SEEKING_AbsolutePositioning
             && (current_flags & 0x7) != AM_SEEKING_NoPositioning)
diff --git a/dlls/quartz/tests/avidec.c b/dlls/quartz/tests/avidec.c
index 2df7a57b273f..4edb027e5ae0 100644
--- a/dlls/quartz/tests/avidec.c
+++ b/dlls/quartz/tests/avidec.c
@@ -1035,13 +1035,13 @@ static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample
     else if (testmode == 1)
     {
         ok(hr == VFW_S_NO_STOP_TIME, "Got hr %#lx.\n", hr);
-        ok(start == 20000, "Got start time %s.\n", wine_dbgstr_longlong(start));
+        ok(start == 20000, "Got start time %I64d.\n", start);
     }
     else
     {
         ok(hr == S_OK, "Got hr %#lx.\n", hr);
-        ok(start == 20000, "Got start time %s.\n", wine_dbgstr_longlong(start));
-        ok(stop == 30000, "Got stop time %s.\n", wine_dbgstr_longlong(stop));
+        ok(start == 20000, "Got start time %I64d.\n", start);
+        ok(stop == 30000, "Got stop time %I64d.\n", stop);
     }
 
     hr = IMediaSample_GetMediaTime(sample, &start, &stop);
@@ -1074,8 +1074,8 @@ static HRESULT testsink_new_segment(struct strmbase_sink *iface,
 {
     struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter);
     ++filter->got_new_segment;
-    ok(start == 10000, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(stop == 20000, "Got stop %s.\n", wine_dbgstr_longlong(stop));
+    ok(start == 10000, "Got start %I64d.\n", start);
+    ok(stop == 20000, "Got stop %I64d.\n", stop);
     ok(rate == 1.0, "Got rate %.16e.\n", rate);
     return S_OK;
 }
diff --git a/dlls/quartz/tests/avisplit.c b/dlls/quartz/tests/avisplit.c
index 70e9b14d9028..ddba8e790cdb 100644
--- a/dlls/quartz/tests/avisplit.c
+++ b/dlls/quartz/tests/avisplit.c
@@ -1630,13 +1630,11 @@ static void test_seeking(void)
         if (hr == S_OK)
         {
             todo_wine_if(time_format_tests[i].wine_todo)
-                ok(result == time_format_tests[i].target_time, "wrong value %s at test %u.\n",
-                        wine_dbgstr_longlong(result), i);
+                ok(result == time_format_tests[i].target_time, "Got time %I64d for test %u.\n", result, i);
         }
         else
         {
-            ok(result == 0xdeadbeef, "result was modified at test %u: %s.\n",
-                    i, wine_dbgstr_longlong(result));
+            ok(result == 0xdeadbeef, "Got time %I64d for test %u.\n", result, i);
         }
     }
 
@@ -1665,37 +1663,34 @@ static void test_seeking(void)
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetStopPosition(seeking, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(stop == duration, "Expected time %s, got %s.\n",
-            wine_dbgstr_longlong(duration), wine_dbgstr_longlong(stop));
+    ok(stop == duration, "Expected time %I64d, got %I64d.\n", duration, stop);
     hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
+    ok(!current, "Got time %I64d.\n", current);
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == duration, "Expected time %s, got %s.\n",
-            wine_dbgstr_longlong(duration), wine_dbgstr_longlong(stop));
+    ok(!current, "Got time %I64d.\n", current);
+    ok(stop == duration, "Expected time %I64d, got %I64d.\n", duration, stop);
 
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 123, &TIME_FORMAT_FRAME);
     todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    todo_wine ok(time == 123 * one_second, "Got time %s.\n", wine_dbgstr_longlong(time));
+    todo_wine ok(time == 123 * one_second, "Got time %I64d.\n", time);
 
     earliest = latest = 0xdeadbeef;
     hr = IMediaSeeking_GetAvailable(seeking, &earliest, &latest);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!earliest, "Got time %s.\n", wine_dbgstr_longlong(earliest));
-    ok(latest == duration, "Expected time %s, got %s.\n",
-            wine_dbgstr_longlong(duration), wine_dbgstr_longlong(latest));
+    ok(!earliest, "Got time %I64d.\n", earliest);
+    ok(latest == duration, "Expected time %I64d, got %I64d.\n", duration, latest);
 
     rate = 0;
     hr = IMediaSeeking_GetRate(seeking, &rate);
@@ -1720,23 +1715,23 @@ static void test_seeking(void)
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
             &stop, AM_SEEKING_AbsolutePositioning);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 1500 * one_msec, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 3500 * one_msec, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 1500 * one_msec, "Got time %I64d.\n", current);
+    ok(stop == 3500 * one_msec, "Got time %I64d.\n", stop);
 
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     /* Native snaps to the nearest frame. */
-    ok(current > 0 && current < duration, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop > 0 && stop < duration && stop > current, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current > 0 && current < duration, "Got time %I64d.\n", current);
+    ok(stop > 0 && stop < duration && stop > current, "Got time %I64d.\n", stop);
 
     current = 1500 * one_msec;
     stop = 3500 * one_msec;
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
             &stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current > 0 && current < duration, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop > 0 && stop < duration && stop > current, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current > 0 && current < duration, "Got time %I64d.\n", current);
+    ok(stop > 0 && stop < duration && stop > current, "Got time %I64d.\n", stop);
 
     hr = IMediaSeeking_GetStopPosition(seeking, &prev_stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -1749,9 +1744,8 @@ static void test_seeking(void)
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == prev_stop, "Expected time %s, got %s.\n",
-            wine_dbgstr_longlong(prev_stop), wine_dbgstr_longlong(stop));
+    ok(!current, "Got time %I64d.\n", current);
+    ok(stop == prev_stop, "Expected time %I64d, got %I64d.\n", prev_stop, stop);
 
     IMediaSeeking_Release(seeking);
     IPin_Release(pin);
diff --git a/dlls/quartz/tests/filesource.c b/dlls/quartz/tests/filesource.c
index 268b265ac771..229eef40b98a 100644
--- a/dlls/quartz/tests/filesource.c
+++ b/dlls/quartz/tests/filesource.c
@@ -1044,8 +1044,8 @@ static void test_async_reader(void)
 
     hr = IAsyncReader_Length(reader, &length, &available);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(length == 600, "Got length %s.\n", wine_dbgstr_longlong(length));
-    ok(available == 600, "Got available length %s.\n", wine_dbgstr_longlong(available));
+    ok(length == 600, "Got length %I64d.\n", length);
+    ok(available == 600, "Got available length %I64d.\n", available);
 
     memset(buffer, 0xcc, sizeof(buffer));
     hr = IAsyncReader_SyncRead(reader, 0, 10, buffer);
diff --git a/dlls/quartz/tests/filtergraph.c b/dlls/quartz/tests/filtergraph.c
index 087c37be5384..fad2d47990ce 100644
--- a/dlls/quartz/tests/filtergraph.c
+++ b/dlls/quartz/tests/filtergraph.c
@@ -358,30 +358,29 @@ static void test_media_seeking(IFilterGraph2 *graph)
     pos = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, NULL, 0x123456789a, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
+    ok(pos == 0x123456789a, "Got time %I64d.\n", pos);
 
     pos = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
+    ok(pos == 0x123456789a, "Got time %I64d.\n", pos);
 
     pos = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
+    ok(pos == 0x123456789a, "Got time %I64d.\n", pos);
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(pos == 0, "got %s\n", wine_dbgstr_longlong(pos));
+    ok(pos == 0, "Got time %I64d.\n", pos);
 
     hr = IMediaSeeking_GetDuration(seeking, &duration);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(duration > 0, "got %s\n", wine_dbgstr_longlong(duration));
+    ok(duration > 0, "Got duration %I64d.\n", duration);
 
     hr = IMediaSeeking_GetStopPosition(seeking, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(stop == duration || stop == duration + 1, "expected %s, got %s\n",
-        wine_dbgstr_longlong(duration), wine_dbgstr_longlong(stop));
+    ok(stop == duration || stop == duration + 1, "Expected time %I64d, got %I64d.\n", duration, stop);
 
     hr = IMediaSeeking_SetPositions(seeking, NULL, AM_SEEKING_ReturnTime, NULL, AM_SEEKING_NoPositioning);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -396,7 +395,7 @@ static void test_media_seeking(IFilterGraph2 *graph)
     pos = 0xdeadbeef;
     hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(pos == 0, "Position != 0 (%s)\n", wine_dbgstr_longlong(pos));
+    ok(pos == 0, "Got time %I64d.\n", pos);
     IFilterGraph2_SetDefaultSyncSource(graph);
 
     IMediaSeeking_Release(seeking);
@@ -527,7 +526,7 @@ static void test_media_event(IFilterGraph2 *graph)
     hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     flaky_wine
-    ok(current == stop, "expected %s, got %s\n", wine_dbgstr_longlong(stop), wine_dbgstr_longlong(current));
+    ok(current == stop, "Expected %I64d, got %I64d.\n", stop, current);
 
     hr = IMediaControl_Pause(control);
     ok(SUCCEEDED(hr), "Got hr %#lx.\n", hr);
@@ -546,7 +545,7 @@ static void test_media_event(IFilterGraph2 *graph)
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current != stop, "Got current position %s.\n", wine_dbgstr_longlong(current));
+    ok(current != stop, "Got current position %I64d.\n", current);
 
     hr = IMediaControl_Stop(control);
     ok(SUCCEEDED(hr), "Got hr %#lx.\n", hr);
@@ -1470,7 +1469,7 @@ static HRESULT WINAPI testfilter_Pause(IBaseFilter *iface)
 static HRESULT WINAPI testfilter_Run(IBaseFilter *iface, REFERENCE_TIME start)
 {
     struct testfilter *filter = impl_from_IBaseFilter(iface);
-    if (winetest_debug > 1) trace("%p->Run(%s)\n", filter, wine_dbgstr_longlong(start));
+    if (winetest_debug > 1) trace("%p->Run(%I64d)\n", filter, start);
 
     ok(filter->state == filter->expect_run_prev, "Expected previous state %#x, got %#x.\n",
             filter->expect_run_prev, filter->state);
@@ -1748,8 +1747,8 @@ static HRESULT WINAPI testseek_SetPositions(IMediaSeeking *iface, LONGLONG *curr
     DWORD current_flags, LONGLONG *stop, DWORD stop_flags )
 {
     struct testfilter *filter = impl_from_IMediaSeeking(iface);
-    if (winetest_debug > 1) trace("%p->SetPositions(%s, %#lx, %s, %#lx)\n",
-            iface, wine_dbgstr_longlong(*current), current_flags, wine_dbgstr_longlong(*stop), stop_flags);
+    if (winetest_debug > 1) trace("%p->SetPositions(%I64d, %#lx, %I64d, %#lx)\n",
+            iface, *current, current_flags, *stop, stop_flags);
     ok(filter->state != State_Running, "Filter should be paused or stopped while seeking.\n");
     filter->seek_current = *current;
     filter->seek_stop = *stop;
@@ -3527,10 +3526,9 @@ static void test_filter_state(void)
     check_filter_state(graph, State_Running);
     if (winetest_interactive) /* Timing problems make this test too liable to fail. */
         ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
-                "Expected time near %s, got %s.\n",
-                wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
-    ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
-        wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
+                "Expected time near %I64d, got %I64d.\n", start_time, source.start_time);
+    ok(sink.start_time == source.start_time, "Expected time %I64d, got %I64d.\n",
+            source.start_time, sink.start_time);
 
     hr = IMediaControl_Pause(control);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -3645,8 +3643,8 @@ static void test_filter_state(void)
     hr = IMediaFilter_Run(filter, 0xdeadbeef);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     check_filter_state(graph, State_Running);
-    ok(source.start_time == 0xdeadbeef, "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
-    ok(sink.start_time == 0xdeadbeef, "Got time %s.\n", wine_dbgstr_longlong(sink.start_time));
+    ok(source.start_time == 0xdeadbeef, "Got time %I64d.\n", source.start_time);
+    ok(sink.start_time == 0xdeadbeef, "Got time %I64d.\n", sink.start_time);
 
     hr = IMediaFilter_Pause(filter);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -3655,8 +3653,8 @@ static void test_filter_state(void)
     hr = IMediaFilter_Run(filter, 0xdeadf00d);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     check_filter_state(graph, State_Running);
-    ok(source.start_time == 0xdeadf00d, "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
-    ok(sink.start_time == 0xdeadf00d, "Got time %s.\n", wine_dbgstr_longlong(sink.start_time));
+    ok(source.start_time == 0xdeadf00d, "Got time %I64d.\n", source.start_time);
+    ok(sink.start_time == 0xdeadf00d, "Got time %I64d.\n", sink.start_time);
 
     hr = IMediaFilter_Pause(filter);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -3674,10 +3672,9 @@ static void test_filter_state(void)
     check_filter_state(graph, State_Running);
     if (winetest_interactive) /* Timing problems make this test too liable to fail. */
         ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
-                "Expected time near %s, got %s.\n",
-                wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
-    ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
-        wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
+                "Expected time near %I64d, got %I64d.\n", start_time, source.start_time);
+    ok(sink.start_time == source.start_time, "Expected time %I64d, got %I64d.\n",
+            source.start_time, sink.start_time);
 
     Sleep(600);
     hr = IMediaFilter_Pause(filter);
@@ -3690,10 +3687,9 @@ static void test_filter_state(void)
     check_filter_state(graph, State_Running);
     if (winetest_interactive) /* Timing problems make this test too liable to fail. */
         ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
-                "Expected time near %s, got %s.\n",
-                wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
-    ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
-        wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
+                "Expected time near %I64d, got %I64d.\n", start_time, source.start_time);
+    ok(sink.start_time == source.start_time, "Expected time %I64d, got %I64d.\n",
+            source.start_time, sink.start_time);
 
     hr = IMediaFilter_Pause(filter);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -3706,10 +3702,9 @@ static void test_filter_state(void)
     check_filter_state(graph, State_Running);
     if (winetest_interactive) /* Timing problems make this test too liable to fail. */
         ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
-                "Expected time near %s, got %s.\n",
-                wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
-    ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
-        wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
+                "Expected time near %I64d, got %I64d.\n", start_time, source.start_time);
+    ok(sink.start_time == source.start_time, "Expected time %I64d, got %I64d.\n",
+            source.start_time, sink.start_time);
 
     hr = IMediaFilter_Stop(filter);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -3724,9 +3719,9 @@ static void test_filter_state(void)
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     check_filter_state(graph, State_Running);
     todo_wine ok(source.start_time > 0 && source.start_time < 500 * 10000,
-            "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
-    ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
-        wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
+            "Got time %I64d.\n", source.start_time);
+    ok(sink.start_time == source.start_time, "Expected time %I64d, got %I64d.\n",
+            source.start_time, sink.start_time);
 
     hr = IMediaControl_Stop(control);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -4607,22 +4602,22 @@ static void test_graph_seeking(void)
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
 
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
 
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
 
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
 
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_NONE, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
     todo_wine ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr);
@@ -4636,12 +4631,12 @@ static void test_graph_seeking(void)
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_NONE, 0x123456789a, &TIME_FORMAT_NONE);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
 
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &testguid);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
 
     hr = IMediaSeeking_GetDuration(seeking, NULL);
     ok(hr == E_POINTER, "Got hr %#lx.\n", hr);
@@ -4665,14 +4660,14 @@ static void test_graph_seeking(void)
     current = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!current, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(!current, "Got time %I64d.\n", time);
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, NULL);
     ok(hr == E_POINTER, "Got hr %#lx.\n", hr);
     current = 0xdeadbeef;
     hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!current, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(!current, "Got time %I64d.\n", time);
 
     hr = IMediaSeeking_GetAvailable(seeking, &earliest, &latest);
     todo_wine ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr);
@@ -4843,7 +4838,7 @@ static void test_graph_seeking(void)
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &testguid);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
 
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &TIME_FORMAT_NONE);
     todo_wine ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr);
@@ -4855,24 +4850,24 @@ static void test_graph_seeking(void)
     filter2.seek_duration = 0x23456;
     hr = IMediaSeeking_GetDuration(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x23456, "Got time %I64d.\n", time);
 
     filter2.seek_duration = 0x12345;
     filter1.seek_duration = 0x23456;
     hr = IMediaSeeking_GetDuration(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x23456, "Got time %I64d.\n", time);
 
     filter1.seek_hr = filter2.seek_hr = 0xbeef;
     hr = IMediaSeeking_GetDuration(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x23456, "Got time %I64d.\n", time);
 
     filter1.seek_hr = E_NOTIMPL;
     filter2.seek_hr = S_OK;
     hr = IMediaSeeking_GetDuration(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x12345, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x12345, "Got time %I64d.\n", time);
 
     filter1.seek_hr = 0xdeadbeef;
     hr = IMediaSeeking_GetDuration(seeking, &time);
@@ -4890,24 +4885,24 @@ static void test_graph_seeking(void)
     filter2.seek_stop = 0x65432;
     hr = IMediaSeeking_GetStopPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x65432, "Got time %I64d.\n", time);
 
     filter2.seek_stop = 0x54321;
     filter1.seek_stop = 0x65432;
     hr = IMediaSeeking_GetStopPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x65432, "Got time %I64d.\n", time);
 
     filter1.seek_hr = filter2.seek_hr = 0xbeef;
     hr = IMediaSeeking_GetStopPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x65432, "Got time %I64d.\n", time);
 
     filter1.seek_hr = E_NOTIMPL;
     filter2.seek_hr = S_OK;
     hr = IMediaSeeking_GetStopPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x54321, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x54321, "Got time %I64d.\n", time);
 
     filter1.seek_hr = 0xdeadbeef;
     hr = IMediaSeeking_GetStopPosition(seeking, &time);
@@ -4923,7 +4918,7 @@ static void test_graph_seeking(void)
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!time, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(!time, "Got time %I64d.\n", time);
 
     flush_cached_seeking(graph, &filter1);
     flush_cached_seeking(graph, &filter2);
@@ -4931,8 +4926,8 @@ static void test_graph_seeking(void)
     current = stop = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(!current, "Got time %I64d.\n", current);
+    ok(stop == 0x65432, "Got time %I64d.\n", stop);
 
     flush_cached_seeking(graph, &filter1);
     flush_cached_seeking(graph, &filter2);
@@ -4942,12 +4937,12 @@ static void test_graph_seeking(void)
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
             &stop, AM_SEEKING_AbsolutePositioning);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
-    ok(filter1.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
-    ok(filter1.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
-    ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
-    ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
+    ok(current == 0x123, "Got time %I64d.\n", current);
+    ok(stop == 0x321, "Got time %I64d.\n", stop);
+    ok(filter1.seek_current == 0x123, "Got time %I64d.\n", filter1.seek_current);
+    ok(filter1.seek_stop == 0x321, "Got time %I64d.\n", filter1.seek_stop);
+    ok(filter2.seek_current == 0x123, "Got time %I64d.\n", filter2.seek_current);
+    ok(filter2.seek_stop == 0x321, "Got time %I64d.\n", filter2.seek_stop);
 
     filter1.seek_hr = filter2.seek_hr = 0xbeef;
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
@@ -4973,37 +4968,37 @@ static void test_graph_seeking(void)
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 12340000, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 12340000, "Got time %I64d.\n", time);
 
     current = stop = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 12340000, "Got time %I64d.\n", current);
+    ok(stop == 0x321, "Got time %I64d.\n", stop);
 
     current = 0x123;
     stop = 0x321;
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
             &stop, AM_SEEKING_AbsolutePositioning);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
-    ok(filter1.seek_current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
-    ok(filter1.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
-    ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
-    ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
+    ok(current == 12340000, "Got time %I64d.\n", current);
+    ok(stop == 0x321, "Got time %I64d.\n", stop);
+    ok(filter1.seek_current == 12340000, "Got time %I64d.\n", filter1.seek_current);
+    ok(filter1.seek_stop == 0x321, "Got time %I64d.\n", filter1.seek_stop);
+    ok(filter2.seek_current == 0x123, "Got time %I64d.\n", filter2.seek_current);
+    ok(filter2.seek_stop == 0x321, "Got time %I64d.\n", filter2.seek_stop);
 
     current = 0x123;
     stop = 0x321;
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
             &stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 43210000, "Got time %s.\n", wine_dbgstr_longlong(stop));
-    ok(filter1.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
-    ok(filter1.seek_stop == 43210000, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
-    ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
-    ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
+    ok(current == 0x123, "Got time %I64d.\n", current);
+    ok(stop == 43210000, "Got time %I64d.\n", stop);
+    ok(filter1.seek_current == 0x123, "Got time %I64d.\n", filter1.seek_current);
+    ok(filter1.seek_stop == 43210000, "Got time %I64d.\n", filter1.seek_stop);
+    ok(filter2.seek_current == 0x123, "Got time %I64d.\n", filter2.seek_current);
+    ok(filter2.seek_stop == 0x321, "Got time %I64d.\n", filter2.seek_stop);
 
     flush_cached_seeking(graph, &filter1);
     flush_cached_seeking(graph, &filter2);
@@ -5051,14 +5046,14 @@ static void test_graph_seeking(void)
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     if (winetest_interactive) /* Timing problems make this test too liable to fail. */
         ok(compare_time(time, 1234 * 10000, 40 * 10000),
-                "Expected about 1234ms, got %s.\n", wine_dbgstr_longlong(time));
+                "Expected about 1234ms, got %I64d.\n", time);
     current = stop = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     if (winetest_interactive) /* Timing problems make this test too liable to fail. */
         ok(compare_time(current, 1234 * 10000, 40 * 10000),
-                "Expected about 1234ms, got %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 9000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+                "Expected about 1234ms, got %I64d.\n", current);
+    ok(stop == 9000 * 10000, "Got time %I64d.\n", stop);
 
     /* This remains true even if NoFlush is specified. */
     current = 1000 * 10000;
@@ -5074,14 +5069,14 @@ static void test_graph_seeking(void)
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     if (winetest_interactive) /* Timing problems make this test too liable to fail. */
         ok(compare_time(time, 1334 * 10000, 80 * 10000),
-                "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(time));
+                "Expected about 1334ms, got %I64d.\n", time);
     current = stop = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     if (winetest_interactive) /* Timing problems make this test too liable to fail. */
         ok(compare_time(current, 1334 * 10000, 80 * 10000),
-                "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 8000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+                "Expected about 1334ms, got %I64d.\n", current);
+    ok(stop == 8000 * 10000, "Got time %I64d.\n", stop);
 
     hr = IMediaControl_Pause(control);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -5094,20 +5089,20 @@ static void test_graph_seeking(void)
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     if (winetest_interactive) /* Timing problems make this test too liable to fail. */
         ok(compare_time(time, 1334 * 10000, 80 * 10000),
-                "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(time));
+                "Expected about 1334ms, got %I64d.\n", time);
     current = stop = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     if (winetest_interactive) /* Timing problems make this test too liable to fail. */
         ok(compare_time(current, 1334 * 10000, 80 * 10000),
-                "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 8000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+                "Expected about 1334ms, got %I64d.\n", current);
+    ok(stop == 8000 * 10000, "Got time %I64d.\n", stop);
 
     hr = IMediaControl_Stop(control);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 12340000, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 12340000, "Got time %I64d.\n", time);
 
     hr = IMediaFilter_SetSyncSource(filter, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -5118,12 +5113,12 @@ static void test_graph_seeking(void)
     Sleep(100);
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    todo_wine ok(!time, "Got time %s.\n", wine_dbgstr_longlong(time));
+    todo_wine ok(!time, "Got time %I64d.\n", time);
     current = stop = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    todo_wine ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(!stop, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    todo_wine ok(!current, "Got time %I64d.\n", current);
+    ok(!stop, "Got time %I64d.\n", stop);
 
     hr = IMediaControl_Stop(control);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -5142,21 +5137,21 @@ static void test_graph_seeking(void)
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time < 5000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time < 5000 * 10000, "Got time %I64d.\n", time);
 
     hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter1.IBaseFilter_iface);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time < 5000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time < 5000 * 10000, "Got time %I64d.\n", time);
 
     hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter2.IBaseFilter_iface);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 6000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 6000 * 10000, "Got time %I64d.\n", time);
 
     hr = IMediaControl_Stop(control);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -5169,7 +5164,7 @@ static void test_graph_seeking(void)
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time < 5000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time < 5000 * 10000, "Got time %I64d.\n", time);
 
     hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter1.IBaseFilter_iface);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -5179,7 +5174,7 @@ static void test_graph_seeking(void)
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 6000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 6000 * 10000, "Got time %I64d.\n", time);
 
     hr = IMediaSeeking_SetTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
diff --git a/dlls/quartz/tests/memallocator.c b/dlls/quartz/tests/memallocator.c
index 37df9a24bd2a..5efd565c35a3 100644
--- a/dlls/quartz/tests/memallocator.c
+++ b/dlls/quartz/tests/memallocator.c
@@ -194,8 +194,8 @@ static void test_sample_time(void)
     end = 0xdeadf00d;
     hr = IMediaSample_GetTime(sample, &start, &end);
     ok(hr == VFW_E_SAMPLE_TIME_NOT_SET, "Got hr %#lx.\n", hr);
-    ok(start == 0xdeadbeef, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(end == 0xdeadf00d, "Got end %s.\n", wine_dbgstr_longlong(end));
+    ok(start == 0xdeadbeef, "Got start %I64d.\n", start);
+    ok(end == 0xdeadf00d, "Got end %I64d.\n", end);
 
     hr = IMediaSample2_GetProperties(sample2, sizeof(props), (BYTE *)&props);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -218,13 +218,13 @@ static void test_sample_time(void)
     start = end = 0;
     hr = IMediaSample_GetTime(sample, &start, &end);
     ok(hr == VFW_S_NO_STOP_TIME, "Got hr %#lx.\n", hr);
-    ok(start == 0x123, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(end == 0x124, "Got end %s.\n", wine_dbgstr_longlong(end));
+    ok(start == 0x123, "Got start %I64d.\n", start);
+    ok(end == 0x124, "Got end %I64d.\n", end);
 
     hr = IMediaSample2_GetProperties(sample2, sizeof(props), (BYTE *)&props);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     ok(props.dwSampleFlags == AM_SAMPLE_TIMEVALID, "Got flags %#lx.\n", props.dwSampleFlags);
-    ok(props.tStart == 0x123, "Got start %s.\n", wine_dbgstr_longlong(props.tStart));
+    ok(props.tStart == 0x123, "Got start %I64d.\n", props.tStart);
 
     hr = IMediaSample_SetTime(sample, NULL, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -255,15 +255,15 @@ static void test_sample_time(void)
     start = end = 0;
     hr = IMediaSample_GetTime(sample, &start, &end);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start == 0x123, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(end == 0x321, "Got end %s.\n", wine_dbgstr_longlong(end));
+    ok(start == 0x123, "Got start %I64d.\n", start);
+    ok(end == 0x321, "Got end %I64d.\n", end);
 
     hr = IMediaSample2_GetProperties(sample2, sizeof(props), (BYTE *)&props);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     ok(props.dwSampleFlags == (AM_SAMPLE_TIMEVALID | AM_SAMPLE_STOPVALID),
             "Got flags %#lx.\n", props.dwSampleFlags);
-    ok(props.tStart == 0x123, "Got start %s.\n", wine_dbgstr_longlong(props.tStart));
-    ok(props.tStop == 0x321, "Got end %s.\n", wine_dbgstr_longlong(props.tStop));
+    ok(props.tStart == 0x123, "Got start %I64d.\n", props.tStart);
+    ok(props.tStop == 0x321, "Got end %I64d.\n", props.tStop);
 
     props.dwSampleFlags = 0;
     hr = IMediaSample2_SetProperties(sample2, sizeof(props), (BYTE *)&props);
@@ -284,13 +284,13 @@ static void test_sample_time(void)
     start = end = 0;
     hr = IMediaSample_GetTime(sample, &start, &end);
     ok(hr == VFW_S_NO_STOP_TIME, "Got hr %#lx.\n", hr);
-    ok(start == 0x123, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(end == 0x124, "Got end %s.\n", wine_dbgstr_longlong(end));
+    ok(start == 0x123, "Got start %I64d.\n", start);
+    ok(end == 0x124, "Got end %I64d.\n", end);
 
     hr = IMediaSample2_GetProperties(sample2, sizeof(props), (BYTE *)&props);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     ok(props.dwSampleFlags == AM_SAMPLE_TIMEVALID, "Got flags %#lx.\n", props.dwSampleFlags);
-    ok(props.tStart == 0x123, "Got start %s.\n", wine_dbgstr_longlong(props.tStart));
+    ok(props.tStart == 0x123, "Got start %I64d.\n", props.tStart);
 
     props.dwSampleFlags = AM_SAMPLE_TIMEVALID | AM_SAMPLE_STOPVALID;
     props.tStart = 0x1234;
@@ -301,15 +301,15 @@ static void test_sample_time(void)
     start = end = 0;
     hr = IMediaSample_GetTime(sample, &start, &end);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start == 0x1234, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(end == 0x4321, "Got end %s.\n", wine_dbgstr_longlong(end));
+    ok(start == 0x1234, "Got start %I64d.\n", start);
+    ok(end == 0x4321, "Got end %I64d.\n", end);
 
     hr = IMediaSample2_GetProperties(sample2, sizeof(props), (BYTE *)&props);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     ok(props.dwSampleFlags == (AM_SAMPLE_TIMEVALID | AM_SAMPLE_STOPVALID),
             "Got flags %#lx.\n", props.dwSampleFlags);
-    ok(props.tStart == 0x1234, "Got start %s.\n", wine_dbgstr_longlong(props.tStart));
-    ok(props.tStop == 0x4321, "Got end %s.\n", wine_dbgstr_longlong(props.tStop));
+    ok(props.tStart == 0x1234, "Got start %I64d.\n", props.tStart);
+    ok(props.tStop == 0x4321, "Got end %I64d.\n", props.tStop);
 
     IMediaSample2_Release(sample2);
     IMediaSample_Release(sample);
@@ -325,8 +325,8 @@ static void test_sample_time(void)
     end = 0xdeadf00d;
     hr = IMediaSample_GetTime(sample, &start, &end);
     ok(hr == VFW_E_SAMPLE_TIME_NOT_SET, "Got hr %#lx.\n", hr);
-    ok(start == 0xdeadbeef, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(end == 0xdeadf00d, "Got end %s.\n", wine_dbgstr_longlong(end));
+    ok(start == 0xdeadbeef, "Got start %I64d.\n", start);
+    ok(end == 0xdeadf00d, "Got end %I64d.\n", end);
 
     hr = IMediaSample2_GetProperties(sample2, sizeof(props), (BYTE *)&props);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -365,8 +365,8 @@ static void test_media_time(void)
     end = 0xdeadf00d;
     hr = IMediaSample_GetMediaTime(sample, &start, &end);
     ok(hr == VFW_E_MEDIA_TIME_NOT_SET, "Got hr %#lx.\n", hr);
-    ok(start == 0xdeadbeef, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(end == 0xdeadf00d, "Got end %s.\n", wine_dbgstr_longlong(end));
+    ok(start == 0xdeadbeef, "Got start %I64d.\n", start);
+    ok(end == 0xdeadf00d, "Got end %I64d.\n", end);
 
     hr = IMediaSample_SetMediaTime(sample, NULL, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -397,8 +397,8 @@ static void test_media_time(void)
     start = end = 0;
     hr = IMediaSample_GetMediaTime(sample, &start, &end);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(start == 0x123, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(end == 0x321, "Got end %s.\n", wine_dbgstr_longlong(end));
+    ok(start == 0x123, "Got start %I64d.\n", start);
+    ok(end == 0x321, "Got end %I64d.\n", end);
 
     IMediaSample_Release(sample);
 
@@ -411,8 +411,8 @@ static void test_media_time(void)
     end = 0xdeadf00d;
     hr = IMediaSample_GetMediaTime(sample, &start, &end);
     ok(hr == VFW_E_MEDIA_TIME_NOT_SET, "Got hr %#lx.\n", hr);
-    ok(start == 0xdeadbeef, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(end == 0xdeadf00d, "Got end %s.\n", wine_dbgstr_longlong(end));
+    ok(start == 0xdeadbeef, "Got start %I64d.\n", start);
+    ok(end == 0xdeadf00d, "Got end %I64d.\n", end);
 
     start = 0x123;
     end = 0x321;
@@ -473,7 +473,7 @@ static void test_sample_properties(void)
     ok(!props.dwTypeSpecificFlags, "Got type-specific flags %#lx.\n", props.dwTypeSpecificFlags);
     ok(!props.dwSampleFlags, "Got flags %#lx.\n", props.dwSampleFlags);
     ok(props.lActual == 65536, "Got actual length %ld.\n", props.lActual);
-    ok(!props.tStart, "Got sample start %s.\n", wine_dbgstr_longlong(props.tStart));
+    ok(!props.tStart, "Got sample start %I64d.\n", props.tStart);
     ok(!props.dwStreamId, "Got stream ID %#lx.\n", props.dwStreamId);
     ok(!props.pMediaType, "Got media type %p.\n", props.pMediaType);
     ok(props.pbBuffer == data, "Expected pointer %p, got %p.\n", data, props.pbBuffer);
@@ -640,7 +640,7 @@ static void test_sample_properties(void)
     ok(!props.dwTypeSpecificFlags, "Got type-specific flags %#lx.\n", props.dwTypeSpecificFlags);
     ok(!props.dwSampleFlags, "Got flags %#lx.\n", props.dwSampleFlags);
     ok(props.lActual == 123, "Got actual length %ld.\n", props.lActual);
-    ok(!props.tStart, "Got sample start %s.\n", wine_dbgstr_longlong(props.tStart));
+    ok(!props.tStart, "Got sample start %I64d.\n", props.tStart);
     ok(!props.dwStreamId, "Got stream ID %#lx.\n", props.dwStreamId);
     ok(!props.pMediaType, "Got media type %p.\n", props.pMediaType);
     ok(props.cbBuffer == 65536, "Got buffer length %ld.\n", props.cbBuffer);
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c
index 804cbbcc3d13..873ea5152130 100644
--- a/dlls/quartz/tests/mpegaudio.c
+++ b/dlls/quartz/tests/mpegaudio.c
@@ -1012,8 +1012,8 @@ static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
     if (filter->got_sample == 1)
     {
-        ok(start == filter->expected_start_time, "Got start time %s.\n", wine_dbgstr_longlong(start));
-        ok(stop == filter->expected_stop_time, "Got stop time %s.\n", wine_dbgstr_longlong(stop));
+        ok(start == filter->expected_start_time, "Got start time %I64d.\n", start);
+        ok(stop == filter->expected_stop_time, "Got stop time %I64d.\n", stop);
     }
 
     return S_OK;
@@ -1024,8 +1024,8 @@ static HRESULT testsink_new_segment(struct strmbase_sink *iface,
 {
     struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter);
     ++filter->got_new_segment;
-    ok(start == 10000, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(stop == 20000, "Got stop %s.\n", wine_dbgstr_longlong(stop));
+    ok(start == 10000, "Got start %I64d.\n", start);
+    ok(stop == 20000, "Got stop %I64d.\n", stop);
     ok(rate == 1.0, "Got rate %.16e.\n", rate);
     return S_OK;
 }
diff --git a/dlls/quartz/tests/mpeglayer3.c b/dlls/quartz/tests/mpeglayer3.c
index 7f7b8bf245f6..a0152767c41c 100644
--- a/dlls/quartz/tests/mpeglayer3.c
+++ b/dlls/quartz/tests/mpeglayer3.c
@@ -1012,8 +1012,8 @@ static HRESULT testsink_new_segment(struct strmbase_sink *iface,
 {
     struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter);
     ++filter->got_new_segment;
-    ok(start == 10000, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(stop == 20000, "Got stop %s.\n", wine_dbgstr_longlong(stop));
+    ok(start == 10000, "Got start %I64d.\n", start);
+    ok(stop == 20000, "Got stop %I64d.\n", stop);
     ok(rate == 1.0, "Got rate %.16e.\n", rate);
     return S_OK;
 }
@@ -1341,10 +1341,9 @@ static void test_sample_processing(IMediaControl *control,
             "Got size %lu.\n", testsink->sample_size);
     ok(testsink->sample_actual == 2304 || testsink->sample_actual == 4608,
             "Got valid size %lu.\n", testsink->sample_actual);
-    ok(testsink->sample_start_time == 0, "Got start time %s.\n",
-            wine_dbgstr_longlong(testsink->sample_start_time));
+    ok(testsink->sample_start_time == 0, "Got start time %I64d.\n", testsink->sample_start_time);
     ok(testsink->sample_stop_time == (testsink->sample_actual == 2304 ? 360000 : 720000),
-            "Got stop time %s.\n", wine_dbgstr_longlong(testsink->sample_stop_time));
+            "Got stop time %I64d.\n", testsink->sample_stop_time);
     testsink->got_sample = 0;
 
     hr = IMediaControl_Stop(control);
@@ -1367,10 +1366,10 @@ static void test_sample_processing(IMediaControl *control,
             "Got size %lu.\n", testsink->sample_size);
     ok(testsink->sample_actual == 2304 || testsink->sample_actual == 4608,
             "Got valid size %lu.\n", testsink->sample_actual);
-    ok(testsink->sample_start_time == 22222, "Got start time %s.\n",
-            wine_dbgstr_longlong(testsink->sample_start_time));
+    ok(testsink->sample_start_time == 22222, "Got start time %I64d.\n",
+            testsink->sample_start_time);
     ok(testsink->sample_stop_time == (testsink->sample_actual == 2304 ? 382222 : 742222),
-            "Got stop time %s.\n", wine_dbgstr_longlong(testsink->sample_stop_time));
+            "Got stop time %I64d.\n", testsink->sample_stop_time);
     testsink->got_sample = 0;
 
     hr = IMediaControl_Stop(control);
@@ -1394,10 +1393,10 @@ static void test_sample_processing(IMediaControl *control,
             "Got size %lu.\n", testsink->sample_size);
     ok(testsink->sample_actual == 2304 || testsink->sample_actual == 4608,
             "Got valid size %lu.\n", testsink->sample_actual);
-    ok(testsink->sample_start_time == 22222, "Got start time %s.\n",
-            wine_dbgstr_longlong(testsink->sample_start_time));
+    ok(testsink->sample_start_time == 22222, "Got start time %I64d.\n",
+            testsink->sample_start_time);
     ok(testsink->sample_stop_time == (testsink->sample_actual == 2304 ? 382222 : 742222),
-            "Got stop time %s.\n", wine_dbgstr_longlong(testsink->sample_stop_time));
+            "Got stop time %I64d.\n", testsink->sample_stop_time);
     testsink->got_sample = 0;
 
     hr = IMediaControl_Stop(control);
diff --git a/dlls/quartz/tests/mpegsplit.c b/dlls/quartz/tests/mpegsplit.c
index 1d62f0000ee0..bebb7d39fcc9 100644
--- a/dlls/quartz/tests/mpegsplit.c
+++ b/dlls/quartz/tests/mpegsplit.c
@@ -1723,33 +1723,30 @@ static void test_seeking(void)
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetStopPosition(seeking, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(stop == duration, "Expected time %s, got %s.\n",
-            wine_dbgstr_longlong(duration), wine_dbgstr_longlong(stop));
+    ok(stop == duration, "Expected time %I64d, got %I64d.\n", duration, stop);
     hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
+    ok(!current, "Got time %I64d.\n", current);
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == duration, "Expected time %s, got %s.\n",
-            wine_dbgstr_longlong(duration), wine_dbgstr_longlong(stop));
+    ok(!current, "Got time %I64d.\n", current);
+    ok(stop == duration, "Expected time %I64d, got %I64d.\n", duration, stop);
 
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
 
     earliest = latest = 0xdeadbeef;
     hr = IMediaSeeking_GetAvailable(seeking, &earliest, &latest);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!earliest, "Got time %s.\n", wine_dbgstr_longlong(earliest));
-    ok(latest == duration, "Expected time %s, got %s.\n",
-            wine_dbgstr_longlong(duration), wine_dbgstr_longlong(latest));
+    ok(!earliest, "Got time %I64d.\n", earliest);
+    ok(latest == duration, "Expected time %I64d, got %I64d.\n", duration, latest);
 
     rate = 0;
     hr = IMediaSeeking_GetRate(seeking, &rate);
@@ -1774,30 +1771,30 @@ static void test_seeking(void)
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
             &stop, AM_SEEKING_AbsolutePositioning);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 200 * 10000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 400 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 200 * 10000, "Got time %I64d.\n", current);
+    ok(stop == 400 * 10000, "Got time %I64d.\n", stop);
 
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 200 * 10000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 400 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 200 * 10000, "Got time %I64d.\n", current);
+    ok(stop == 400 * 10000, "Got time %I64d.\n", stop);
 
     current = 200 * 10000;
     stop = 400 * 10000;
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
             &stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 200 * 10000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 400 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 200 * 10000, "Got time %I64d.\n", current);
+    ok(stop == 400 * 10000, "Got time %I64d.\n", stop);
 
     current = 100 * 10000;
     stop = 200 * 10000;
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
             &stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 100 * 10000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 200 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 100 * 10000, "Got time %I64d.\n", current);
+    ok(stop == 200 * 10000, "Got time %I64d.\n", stop);
 
     current = 50 * 10000;
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
@@ -1807,8 +1804,8 @@ static void test_seeking(void)
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 50 * 10000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 200 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 50 * 10000, "Got time %I64d.\n", current);
+    ok(stop == 200 * 10000, "Got time %I64d.\n", stop);
 
     IMediaSeeking_Release(seeking);
     IPin_Release(pin);
@@ -2192,7 +2189,7 @@ static void test_video_read_position(void)
 
     hr = IFilterGraph2_ConnectDirect(graph, &testsource.source.pin.IPin_iface, sink, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(testsource.read_position == total, "Got 0x%s, expected 0x%s.\n", wine_dbgstr_longlong(testsource.read_position), wine_dbgstr_longlong(total));
+    ok(testsource.read_position == total, "Got %#I64x, expected %#I64x.\n", testsource.read_position, total);
 
     IAsyncReader_Release(testsource.reader);
     IPin_Release(sink);
diff --git a/dlls/quartz/tests/mpegvideo.c b/dlls/quartz/tests/mpegvideo.c
index d5e8a1c30df1..3bcf466e8886 100644
--- a/dlls/quartz/tests/mpegvideo.c
+++ b/dlls/quartz/tests/mpegvideo.c
@@ -954,11 +954,11 @@ static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample
 
     if (filter->got_sample == 0 && filter->expected_start_time != (REFERENCE_TIME)-1)
     {
-        ok(start == filter->expected_start_time, "Got start time %s, expected %s.\n",
-                wine_dbgstr_longlong(start), wine_dbgstr_longlong(filter->expected_start_time));
+        ok(start == filter->expected_start_time, "Got start time %I64d, expected %I64d.\n",
+                start, filter->expected_start_time);
         todo_wine
-            ok(stop == filter->expected_stop_time, "Got stop time %s, expected %s.\n",
-                wine_dbgstr_longlong(stop), wine_dbgstr_longlong(filter->expected_stop_time));
+            ok(stop == filter->expected_stop_time, "Got stop time %I64d, expected %I64d.\n",
+                stop, filter->expected_stop_time);
     }
 
     ++filter->got_sample;
@@ -971,8 +971,8 @@ static HRESULT testsink_new_segment(struct strmbase_sink *iface,
 {
     struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter);
     ++filter->got_new_segment;
-    ok(start == 10000, "Got start %s.\n", wine_dbgstr_longlong(start));
-    ok(stop == 20000, "Got stop %s.\n", wine_dbgstr_longlong(stop));
+    ok(start == 10000, "Got start %I64d.\n", start);
+    ok(stop == 20000, "Got stop %I64d.\n", stop);
     ok(rate == 1.0, "Got rate %.16e.\n", rate);
     return S_OK;
 }
diff --git a/dlls/quartz/tests/systemclock.c b/dlls/quartz/tests/systemclock.c
index 65bc406f35f0..5eea6bce7d99 100644
--- a/dlls/quartz/tests/systemclock.c
+++ b/dlls/quartz/tests/systemclock.c
@@ -173,23 +173,20 @@ static void test_get_time(void)
 
     hr = IReferenceClock_GetTime(clock, &time1);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time1 % 10000 == 0, "Expected no less than 1ms coarseness, but got time %s.\n",
-            wine_dbgstr_longlong(time1));
+    ok(time1 % 10000 == 0, "Expected no less than 1ms coarseness, but got time %I64d.\n", time1);
 
     ticks = (REFERENCE_TIME)timeGetTime() * 10000;
 
     hr = IReferenceClock_GetTime(clock, &time2);
     ok(hr == (time2 == time1 ? S_FALSE : S_OK), "Got hr %#lx.\n", hr);
-    ok(time2 % 10000 == 0, "Expected no less than 1ms coarseness, but got time %s.\n",
-            wine_dbgstr_longlong(time1));
+    ok(time2 % 10000 == 0, "Expected no less than 1ms coarseness, but got time %I64d.\n", time1);
 
     ok(time2 >= ticks && ticks >= time1, "Got timestamps %I64d, %I64d, %I64d.\n", time1, ticks, time2);
 
     Sleep(100);
     hr = IReferenceClock_GetTime(clock, &time2);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time2 - time1 > 80 * 10000, "Expected about %s, but got %s.\n",
-            wine_dbgstr_longlong(time1 + 80 * 10000), wine_dbgstr_longlong(time2));
+    ok(time2 - time1 > 80 * 10000, "Expected about %I64d, but got %I64d.\n", time1 + 80 * 10000, time2);
 
     ref = IReferenceClock_Release(clock);
     ok(!ref, "Got outstanding refcount %ld.\n", ref);
diff --git a/dlls/quartz/tests/videorenderer.c b/dlls/quartz/tests/videorenderer.c
index 09f11a30c900..54681e683043 100644
--- a/dlls/quartz/tests/videorenderer.c
+++ b/dlls/quartz/tests/videorenderer.c
@@ -1044,7 +1044,7 @@ static void test_sample_time(IBaseFilter *filter, IPin *pin, IMemInputPin *input
 
     hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 10000000, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 10000000, "Got time %I64d.\n", time);
 
     ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Thread should block in Receive().\n");
 
diff --git a/dlls/quartz/tests/vmr7.c b/dlls/quartz/tests/vmr7.c
index 0e799efb9603..e1bb00a20de5 100644
--- a/dlls/quartz/tests/vmr7.c
+++ b/dlls/quartz/tests/vmr7.c
@@ -3277,8 +3277,8 @@ static HRESULT WINAPI presenter_PresentImage(IVMRImagePresenter *iface, DWORD_PT
     ok(cookie == 0xabacab, "Got cookie %#Ix.\n", cookie);
     ok(info->dwFlags == VMRSample_TimeValid, "Got flags %#lx.\n", info->dwFlags);
     todo_wine ok(info->lpSurf == presenter->surfaces[5 - presenter->got_PresentImage], "Got unexpected surface.\n");
-    ok(!info->rtStart, "Got start time %s.\n", wine_dbgstr_longlong(info->rtStart));
-    ok(info->rtEnd == 10000000, "Got end time %s.\n", wine_dbgstr_longlong(info->rtEnd));
+    ok(!info->rtStart, "Got start time %I64d.\n", info->rtStart);
+    ok(info->rtEnd == 10000000, "Got end time %I64d.\n", info->rtEnd);
     todo_wine ok(info->szAspectRatio.cx == 120, "Got aspect ratio width %ld.\n", info->szAspectRatio.cx);
     todo_wine ok(info->szAspectRatio.cy == 60, "Got aspect ratio height %ld.\n", info->szAspectRatio.cy);
     ok(EqualRect(&info->rcSrc, &rect), "Got source rect %s.\n", wine_dbgstr_rect(&info->rcSrc));
diff --git a/dlls/quartz/tests/vmr9.c b/dlls/quartz/tests/vmr9.c
index 0d64e813a7fc..1c24a1f70e76 100644
--- a/dlls/quartz/tests/vmr9.c
+++ b/dlls/quartz/tests/vmr9.c
@@ -3019,8 +3019,8 @@ static HRESULT WINAPI presenter_PresentImage(IVMRImagePresenter9 *iface, DWORD_P
     IDirect3DDevice9_Release(device);
     ok(cookie == 0xabacab, "Got cookie %#Ix.\n", cookie);
     todo_wine ok(info->dwFlags == VMR9Sample_TimeValid, "Got flags %#lx.\n", info->dwFlags);
-    ok(!info->rtStart, "Got start time %s.\n", wine_dbgstr_longlong(info->rtStart));
-    ok(info->rtEnd == 10000000, "Got end time %s.\n", wine_dbgstr_longlong(info->rtEnd));
+    ok(!info->rtStart, "Got start time %I64d.\n", info->rtStart);
+    ok(info->rtEnd == 10000000, "Got end time %I64d.\n", info->rtEnd);
     todo_wine ok(info->szAspectRatio.cx == 120, "Got aspect ratio width %ld.\n", info->szAspectRatio.cx);
     todo_wine ok(info->szAspectRatio.cy == 60, "Got aspect ratio height %ld.\n", info->szAspectRatio.cy);
     ok(EqualRect(&info->rcSrc, &rect), "Got source rect %s.\n", wine_dbgstr_rect(&info->rcSrc));
diff --git a/dlls/quartz/tests/waveparser.c b/dlls/quartz/tests/waveparser.c
index 1c86a39c7301..6dc240ff714c 100644
--- a/dlls/quartz/tests/waveparser.c
+++ b/dlls/quartz/tests/waveparser.c
@@ -934,37 +934,34 @@ static void test_seeking(void)
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetStopPosition(seeking, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(stop == duration, "Expected time %s, got %s.\n",
-            wine_dbgstr_longlong(duration), wine_dbgstr_longlong(stop));
+    ok(stop == duration, "Expected time %I64d, got %I64d.\n", duration, stop);
     hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
+    ok(!current, "Got time %I64d.\n", current);
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == duration, "Expected time %s, got %s.\n",
-            wine_dbgstr_longlong(duration), wine_dbgstr_longlong(stop));
+    ok(!current, "Got time %I64d.\n", current);
+    ok(stop == duration, "Expected time %I64d, got %I64d.\n", duration, stop);
 
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
+    ok(time == 0x123456789a, "Got time %I64d.\n", time);
     time = 0xdeadbeef;
     hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 441, &TIME_FORMAT_SAMPLE);
     todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    todo_wine ok(time == 100000, "Got time %s.\n", wine_dbgstr_longlong(time));
+    todo_wine ok(time == 100000, "Got time %I64d.\n", time);
 
     earliest = latest = 0xdeadbeef;
     hr = IMediaSeeking_GetAvailable(seeking, &earliest, &latest);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(!earliest, "Got time %s.\n", wine_dbgstr_longlong(earliest));
-    ok(latest == duration, "Expected time %s, got %s.\n",
-            wine_dbgstr_longlong(duration), wine_dbgstr_longlong(latest));
+    ok(!earliest, "Got time %I64d.\n", earliest);
+    ok(latest == duration, "Expected time %I64d, got %I64d.\n", duration, latest);
 
     rate = 0;
     hr = IMediaSeeking_GetRate(seeking, &rate);
@@ -989,30 +986,30 @@ static void test_seeking(void)
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
             &stop, AM_SEEKING_AbsolutePositioning);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 200 * 10000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 400 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 200 * 10000, "Got time %I64d.\n", current);
+    ok(stop == 400 * 10000, "Got time %I64d.\n", stop);
 
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 200 * 10000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 400 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 200 * 10000, "Got time %I64d.\n", current);
+    ok(stop == 400 * 10000, "Got time %I64d.\n", stop);
 
     current = 200 * 10000;
     stop = 400 * 10000;
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
             &stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 200 * 10000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 400 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 200 * 10000, "Got time %I64d.\n", current);
+    ok(stop == 400 * 10000, "Got time %I64d.\n", stop);
 
     current = 100 * 10000;
     stop = 200 * 10000;
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
             &stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 100 * 10000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 200 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 100 * 10000, "Got time %I64d.\n", current);
+    ok(stop == 200 * 10000, "Got time %I64d.\n", stop);
 
     current = 50 * 10000;
     hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
@@ -1022,8 +1019,8 @@ static void test_seeking(void)
     stop = current = 0xdeadbeef;
     hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ok(current == 50 * 10000, "Got time %s.\n", wine_dbgstr_longlong(current));
-    ok(stop == 200 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
+    ok(current == 50 * 10000, "Got time %I64d.\n", current);
+    ok(stop == 200 * 10000, "Got time %I64d.\n", stop);
 
     IMediaSeeking_Release(seeking);
     IPin_Release(pin);
diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index 1a0c444579be..e704508f2a5b 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -3806,13 +3806,31 @@ LRESULT editor_handle_message( ME_TextEditor *editor, UINT msg, WPARAM wParam,
     int start_ofs, end_ofs;
     ME_Cursor cursor;
 
-    if (wParam > ME_GetTextLength(editor))
-      return 0;
     if (wParam == -1)
     {
-      FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
-      return 0;
+      LONG from, to;
+      int line_start, line_end, line_len, result;
+
+      ME_GetSelectionOfs( editor, &from, &to );
+
+      cursor_from_char_ofs( editor, from, &cursor );
+      row = row_from_cursor( &cursor );
+      row_first_cursor( row, &cursor );
+      line_start = ME_GetCursorOfs( &cursor );
+
+      cursor_from_char_ofs( editor, to, &cursor );
+      row = row_from_cursor( &cursor );
+      row_first_cursor( row, &cursor );
+      line_end = ME_GetCursorOfs( &cursor );
+      row_end_cursor( row, &cursor, FALSE );
+      line_len = ME_GetCursorOfs( &cursor ) - line_end;
+
+      result = (from - line_start) + (line_end + line_len - to);
+      TRACE( "EM_LINELENGTH(-1): sel=%ld..%ld result=%d\n", from, to, result );
+      return result;
     }
+    if (wParam > ME_GetTextLength(editor))
+      return 0;
     cursor_from_char_ofs( editor, wParam, &cursor );
     row = row_from_cursor( &cursor );
     row_first_cursor( row, &cursor );
diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c
index 19cd40aea485..0a7b8ec6ce47 100644
--- a/dlls/riched20/tests/editor.c
+++ b/dlls/riched20/tests/editor.c
@@ -535,6 +535,31 @@ static void test_EM_LINELENGTH(void)
         offset_test[i][0], result, offset_test[i][1]);
   }
 
+  /* EM_LINELENGTH with wParam == -1 returns the count of unselected
+   * characters on the line(s) containing the selection. */
+  SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
+  result = SendMessageA(hwndRichEdit, EM_LINELENGTH, -1, 0);
+  ok(result == 9, "EM_LINELENGTH(-1) with caret at start: got %Id, expected 9\n", result);
+
+  SendMessageA(hwndRichEdit, EM_SETSEL, 5, 5);
+  result = SendMessageA(hwndRichEdit, EM_LINELENGTH, -1, 0);
+  ok(result == 9, "EM_LINELENGTH(-1) with caret mid-line: got %Id, expected 9\n", result);
+
+  /* Selection entirely within one line: unselected chars on that line */
+  SendMessageA(hwndRichEdit, EM_SETSEL, 2, 5);
+  result = SendMessageA(hwndRichEdit, EM_LINELENGTH, -1, 0);
+  ok(result == 6, "EM_LINELENGTH(-1) with sel 2..5 on line 1: got %Id, expected 6\n", result);
+
+  /* Selection spanning two lines: unselected chars on first + last line */
+  SendMessageA(hwndRichEdit, EM_SETSEL, 4, 13);
+  result = SendMessageA(hwndRichEdit, EM_LINELENGTH, -1, 0);
+  ok(result == 10, "EM_LINELENGTH(-1) with sel 4..13: got %Id, expected 10\n", result);
+
+  /* Caret at end of text */
+  SendMessageA(hwndRichEdit, EM_SETSEL, 39, 39);
+  result = SendMessageA(hwndRichEdit, EM_LINELENGTH, -1, 0);
+  ok(result == 9, "EM_LINELENGTH(-1) with caret at end: got %Id, expected 9\n", result);
+
   /* Test with multibyte character */
   if (!is_lang_japanese)
     skip("Skip multibyte character tests on non-Japanese platform\n");
diff --git a/dlls/rtutils/Makefile.in b/dlls/rtutils/Makefile.in
index 488a0fdb1ac4..afef4593f3a0 100644
--- a/dlls/rtutils/Makefile.in
+++ b/dlls/rtutils/Makefile.in
@@ -1,7 +1,5 @@
 MODULE    = rtutils.dll
 IMPORTLIB = rtutils
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	tracing.c
diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c
index c89bffae5a09..3e374357ab1d 100644
--- a/dlls/sapi/tests/tts.c
+++ b/dlls/sapi/tests/tts.c
@@ -494,6 +494,8 @@ static void test_spvoice(void)
     ISpDataKey *attrs_key;
     LONG rate;
     USHORT volume;
+    SPVOICESTATUS status;
+    SPVPRIORITY priority;
     ULONG stream_num;
     DWORD regid;
     WAVEFORMATEX wfx;
@@ -651,6 +653,40 @@ static void test_spvoice(void)
     hr = ISpVoice_SetVolume(voice, 101);
     ok(hr == E_INVALIDARG, "got %#lx.\n", hr);
 
+    hr = ISpVoice_GetStatus(voice, NULL, NULL);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+
+    memset(&status, 0xcc, sizeof(status));
+    hr = ISpVoice_GetStatus(voice, &status, NULL);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+    ok(status.dwRunningState == SPRS_DONE, "got %#lx.\n", status.dwRunningState);
+    ok(status.ulLastStreamQueued == 0, "got %lu.\n", status.ulLastStreamQueued);
+    ok(status.ulCurrentStream == 0, "got %lu.\n", status.ulCurrentStream);
+    ok(status.hrLastResult == S_OK, "got %#lx.\n", status.hrLastResult);
+
+    priority = 0xdead;
+    hr = ISpVoice_GetPriority(voice, &priority);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+    ok(priority == SPVPRI_NORMAL, "got %d.\n", priority);
+
+    hr = ISpVoice_SetPriority(voice, SPVPRI_ALERT);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+
+    priority = 0xdead;
+    hr = ISpVoice_GetPriority(voice, &priority);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+    ok(priority == SPVPRI_ALERT, "got %d.\n", priority);
+
+    hr = ISpVoice_SetPriority(voice, SPVPRI_OVER);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+
+    priority = 0xdead;
+    hr = ISpVoice_GetPriority(voice, &priority);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+    ok(priority == SPVPRI_OVER, "got %d.\n", priority);
+
+    hr = ISpVoice_SetPriority(voice, SPVPRI_NORMAL);
+    ok(hr == S_OK, "got %#lx.\n", hr);
     hr = CoRegisterClassObject(&CLSID_TestEngine, (IUnknown *)&test_engine_cf,
                                CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &regid);
     ok(hr == S_OK, "got %#lx.\n", hr);
@@ -732,6 +768,16 @@ static void test_spvoice(void)
     ok(hr == S_OK, "got %#lx.\n", hr);
     ok(duration < 200, "took %lu ms.\n", duration);
 
+    memset(&status, 0xcc, sizeof(status));
+    hr = ISpVoice_GetStatus(voice, &status, NULL);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+    ok(status.dwRunningState == SPRS_DONE, "got %#lx.\n", status.dwRunningState);
+    ok(status.ulLastStreamQueued == stream_num, "got %lu vs %lu.\n",
+       status.ulLastStreamQueued, stream_num);
+    ok(status.ulCurrentStream == stream_num, "got %lu vs %lu.\n",
+       status.ulCurrentStream, stream_num);
+    ok(status.hrLastResult == S_OK, "got %#lx.\n", status.hrLastResult);
+
     reset_engine_params(&test_engine);
     test_engine.output_data = wave_data;
     test_engine.output_len = wave_len;
@@ -744,6 +790,16 @@ static void test_spvoice(void)
     todo_wine ok(stream_num == 1, "got %lu.\n", stream_num);
     ok(duration < 500, "took %lu ms.\n", duration);
 
+    memset(&status, 0xcc, sizeof(status));
+    hr = ISpVoice_GetStatus(voice, &status, NULL);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+    /* dwRunningState may be 0 transiently on Windows before the worker
+     * picks up the queued task. */
+    ok(status.dwRunningState == SPRS_IS_SPEAKING || status.dwRunningState == 0,
+       "got %#lx.\n", status.dwRunningState);
+    ok(status.ulLastStreamQueued == stream_num, "got %lu vs %lu.\n",
+       status.ulLastStreamQueued, stream_num);
+
     hr = ISpVoice_WaitUntilDone(voice, 100);
     ok(hr == S_FALSE, "got %#lx.\n", hr);
 
@@ -752,6 +808,14 @@ static void test_spvoice(void)
     ok(hr == S_OK, "got %#lx.\n", hr);
     ok(duration > 800 && duration < 3500, "took %lu ms.\n", duration);
 
+    memset(&status, 0xcc, sizeof(status));
+    hr = ISpVoice_GetStatus(voice, &status, NULL);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+    ok(status.dwRunningState == SPRS_DONE, "got %#lx.\n", status.dwRunningState);
+    ok(status.ulCurrentStream == stream_num, "got %lu vs %lu.\n",
+       status.ulCurrentStream, stream_num);
+    ok(status.hrLastResult == S_OK, "got %#lx.\n", status.hrLastResult);
+
     ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n");
     ok(test_engine.flags == SPF_NLP_SPEAK_PUNC, "got %#lx.\n", test_engine.flags);
     ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count);
diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c
index 159635a45595..11de8a7b2faa 100644
--- a/dlls/sapi/tts.c
+++ b/dlls/sapi/tts.c
@@ -58,6 +58,9 @@ struct speech_voice
     LONG rate;
     SPVSTATE state;
     struct async_queue queue;
+    ULONG last_stream_num;
+    HRESULT last_hr;
+    SPVPRIORITY priority;
     CRITICAL_SECTION cs;
 };
 
@@ -959,6 +962,11 @@ static void speak_proc(struct async_task *task)
 
     EnterCriticalSection(&This->cs);
 
+    /* Update ulCurrentStream tracking now, before doing any work — per MSDN
+     * SPVOICESTATUS.ulCurrentStream reports the stream currently being
+     * processed, and sticks at the last processed value once idle. */
+    This->last_stream_num = site->stream_num;
+
     if (This->actions & SPVES_ABORT)
     {
         LeaveCriticalSection(&This->cs);
@@ -997,6 +1005,9 @@ done:
         ISpAudio_Release(audio);
     }
     CoTaskMemFree(wfx);
+    EnterCriticalSection(&This->cs);
+    This->last_hr = hr;
+    LeaveCriticalSection(&This->cs);
     ISpTTSEngine_Release(speak_task->engine);
     free_frag_list(speak_task->frag_list);
     ISpTTSEngineSite_Release(speak_task->site);
@@ -1211,14 +1222,26 @@ static HRESULT WINAPI spvoice_SpeakStream(ISpVoice *iface, IStream *stream, DWOR
 
 static HRESULT WINAPI spvoice_GetStatus(ISpVoice *iface, SPVOICESTATUS *status, WCHAR **bookmark)
 {
-    static unsigned int once;
+    struct speech_voice *This = impl_from_ISpVoice(iface);
 
-    if (!once++)
-        FIXME("(%p, %p, %p): stub.\n", iface, status, bookmark);
-    else
-        WARN("(%p, %p, %p): stub.\n", iface, status, bookmark);
+    TRACE("(%p, %p, %p).\n", iface, status, bookmark);
 
-    return E_NOTIMPL;
+    if (bookmark) *bookmark = NULL;
+
+    /* Windows accepts a NULL status pointer as a no-op returning S_OK. */
+    if (!status) return S_OK;
+
+    memset(status, 0, sizeof(*status));
+
+    EnterCriticalSection(&This->cs);
+    status->ulCurrentStream    = This->last_stream_num;
+    status->ulLastStreamQueued = This->cur_stream_num;
+    status->hrLastResult       = This->last_hr;
+    status->dwRunningState     = (async_wait_queue_empty(&This->queue, 0) == WAIT_OBJECT_0) ?
+                                 SPRS_DONE : SPRS_IS_SPEAKING;
+    LeaveCriticalSection(&This->cs);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI spvoice_Skip(ISpVoice *iface, const WCHAR *type, LONG items, ULONG *skipped)
@@ -1230,16 +1253,33 @@ static HRESULT WINAPI spvoice_Skip(ISpVoice *iface, const WCHAR *type, LONG item
 
 static HRESULT WINAPI spvoice_SetPriority(ISpVoice *iface, SPVPRIORITY priority)
 {
-    FIXME("(%p, %d): stub.\n", iface, priority);
+    struct speech_voice *This = impl_from_ISpVoice(iface);
 
-    return E_NOTIMPL;
+    TRACE("(%p, %d).\n", iface, priority);
+
+    if (priority != SPVPRI_NORMAL && priority != SPVPRI_ALERT && priority != SPVPRI_OVER)
+        return E_INVALIDARG;
+
+    EnterCriticalSection(&This->cs);
+    This->priority = priority;
+    LeaveCriticalSection(&This->cs);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI spvoice_GetPriority(ISpVoice *iface, SPVPRIORITY *priority)
 {
-    FIXME("(%p, %p): stub.\n", iface, priority);
+    struct speech_voice *This = impl_from_ISpVoice(iface);
 
-    return E_NOTIMPL;
+    TRACE("(%p, %p).\n", iface, priority);
+
+    if (!priority) return E_POINTER;
+
+    EnterCriticalSection(&This->cs);
+    *priority = This->priority;
+    LeaveCriticalSection(&This->cs);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI spvoice_SetAlertBoundary(ISpVoice *iface, SPEVENTENUM boundary)
@@ -1714,6 +1754,9 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj)
     This->actions = SPVES_CONTINUE;
     This->volume = 100;
     This->rate = 0;
+    This->last_stream_num = 0;
+    This->last_hr = S_OK;
+    This->priority = SPVPRI_NORMAL;
 
     memset(&This->state, 0, sizeof(This->state));
     This->state.Volume = 100;
diff --git a/dlls/sas/Makefile.in b/dlls/sas/Makefile.in
index a21e053b6c49..32b2ca7a1a9d 100644
--- a/dlls/sas/Makefile.in
+++ b/dlls/sas/Makefile.in
@@ -1,3 +1 @@
 MODULE    = sas.dll
-
-EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/scrrun/dictionary.c b/dlls/scrrun/dictionary.c
index af771c9245a9..1b6188653605 100644
--- a/dlls/scrrun/dictionary.c
+++ b/dlls/scrrun/dictionary.c
@@ -199,24 +199,30 @@ static BOOL is_matching_key(const struct dictionary *dict, const struct keyitem_
     }
 }
 
-static struct keyitem_pair *get_keyitem_pair(struct dictionary *dict, VARIANT *key)
+/* Looks up the pair for a key. On success *ret is the matching pair, or NULL
+   if the key is not present. A failure to hash the key (e.g. an object key
+   that can no longer be queried for IID_IUnknown) is reported to the caller
+   rather than being mistaken for an absent key. */
+static HRESULT get_keyitem_pair(struct dictionary *dict, VARIANT *key, struct keyitem_pair **ret)
 {
     struct keyitem_pair *pair;
     struct list *head, *entry;
     VARIANT hash, v;
     HRESULT hr;
 
+    *ret = NULL;
+
     hr = IDictionary_get_HashVal(&dict->IDictionary_iface, key, &hash);
     if (FAILED(hr))
-        return NULL;
+        return hr;
 
     head = get_bucket_head(dict, V_I4(&hash));
     if (!head->next || list_empty(head))
-        return NULL;
+        return S_OK;
 
     VariantInit(&v);
-    if (FAILED(VariantCopyInd(&v, key)))
-        return NULL;
+    if (FAILED(hr = VariantCopyInd(&v, key)))
+        return hr;
 
     entry = list_head(head);
     do
@@ -225,13 +231,14 @@ static struct keyitem_pair *get_keyitem_pair(struct dictionary *dict, VARIANT *k
         if (is_matching_key(dict, pair, &v, V_I4(&hash)))
         {
             VariantClear(&v);
-            return pair;
+            *ret = pair;
+            return S_OK;
         }
     } while ((entry = list_next(head, entry)));
 
     VariantClear(&v);
 
-    return NULL;
+    return S_OK;
 }
 
 static HRESULT add_keyitem_pair(struct dictionary *dict, VARIANT *key, VARIANT *item)
@@ -563,10 +570,13 @@ static HRESULT WINAPI dictionary_putref_Item(IDictionary *iface, VARIANT *key, V
 {
     struct dictionary *dictionary = impl_from_IDictionary(iface);
     struct keyitem_pair *pair;
+    HRESULT hr;
 
     TRACE("%p, %s, %s.\n", iface, debugstr_variant(key), debugstr_variant(item));
 
-    if ((pair = get_keyitem_pair(dictionary, key)))
+    if (FAILED(hr = get_keyitem_pair(dictionary, key, &pair)))
+        return hr;
+    if (pair)
         return VariantCopyInd(&pair->item, item);
 
     return add_keyitem_pair(dictionary, key, item);
@@ -576,10 +586,13 @@ static HRESULT WINAPI dictionary_put_Item(IDictionary *iface, VARIANT *key, VARI
 {
     struct dictionary *dictionary = impl_from_IDictionary(iface);
     struct keyitem_pair *pair;
+    HRESULT hr;
 
     TRACE("%p, %s, %s.\n", iface, debugstr_variant(key), debugstr_variant(item));
 
-    if ((pair = get_keyitem_pair(dictionary, key)))
+    if (FAILED(hr = get_keyitem_pair(dictionary, key, &pair)))
+        return hr;
+    if (pair)
         return VariantCopyInd(&pair->item, item);
 
     return add_keyitem_pair(dictionary, key, item);
@@ -589,10 +602,13 @@ static HRESULT WINAPI dictionary_get_Item(IDictionary *iface, VARIANT *key, VARI
 {
     struct dictionary *dictionary = impl_from_IDictionary(iface);
     struct keyitem_pair *pair;
+    HRESULT hr;
 
     TRACE("%p, %s, %p.\n", iface, debugstr_variant(key), item);
 
-    if ((pair = get_keyitem_pair(dictionary, key)))
+    if (FAILED(hr = get_keyitem_pair(dictionary, key, &pair)))
+        return hr;
+    if (pair)
         VariantCopy(item, &pair->item);
     else {
         VariantInit(item);
@@ -605,10 +621,14 @@ static HRESULT WINAPI dictionary_get_Item(IDictionary *iface, VARIANT *key, VARI
 static HRESULT WINAPI dictionary_Add(IDictionary *iface, VARIANT *key, VARIANT *item)
 {
     struct dictionary *dictionary = impl_from_IDictionary(iface);
+    struct keyitem_pair *pair;
+    HRESULT hr;
 
     TRACE("%p, %s, %s.\n", iface, debugstr_variant(key), debugstr_variant(item));
 
-    if (get_keyitem_pair(dictionary, key))
+    if (FAILED(hr = get_keyitem_pair(dictionary, key, &pair)))
+        return hr;
+    if (pair)
         return CTL_E_KEY_ALREADY_EXISTS;
 
     return add_keyitem_pair(dictionary, key, item);
@@ -627,13 +647,18 @@ static HRESULT WINAPI dictionary_get_Count(IDictionary *iface, LONG *count)
 static HRESULT WINAPI dictionary_Exists(IDictionary *iface, VARIANT *key, VARIANT_BOOL *exists)
 {
     struct dictionary *dictionary = impl_from_IDictionary(iface);
+    struct keyitem_pair *pair;
+    HRESULT hr;
 
     TRACE("%p, %s, %p.\n", iface, debugstr_variant(key), exists);
 
     if (!exists)
         return CTL_E_ILLEGALFUNCTIONCALL;
 
-    *exists = get_keyitem_pair(dictionary, key) != NULL ? VARIANT_TRUE : VARIANT_FALSE;
+    if (FAILED(hr = get_keyitem_pair(dictionary, key, &pair)))
+        return hr;
+
+    *exists = pair ? VARIANT_TRUE : VARIANT_FALSE;
     return S_OK;
 }
 
@@ -686,7 +711,10 @@ static HRESULT WINAPI dictionary_put_Key(IDictionary *iface, VARIANT *key, VARIA
 
     TRACE("%p, %s, %s.\n", iface, debugstr_variant(key), debugstr_variant(newkey));
 
-    if ((pair = get_keyitem_pair(dictionary, key)))
+    if (FAILED(hr = get_keyitem_pair(dictionary, key, &pair)))
+        return hr;
+
+    if (pair)
     {
         /* found existing pair for a key, add new pair with new key
            and old item and remove old pair after that */
@@ -746,10 +774,13 @@ static HRESULT WINAPI dictionary_Remove(IDictionary *iface, VARIANT *key)
 {
     struct dictionary *dictionary = impl_from_IDictionary(iface);
     struct keyitem_pair *pair;
+    HRESULT hr;
 
     TRACE("%p, %s.\n", iface, debugstr_variant(key));
 
-    if (!(pair = get_keyitem_pair(dictionary, key)))
+    if (FAILED(hr = get_keyitem_pair(dictionary, key, &pair)))
+        return hr;
+    if (!pair)
         return CTL_E_ELEMENT_NOT_FOUND;
 
     notify_remove_pair(&dictionary->notifier, &pair->entry);
diff --git a/dlls/scrrun/tests/dictionary.c b/dlls/scrrun/tests/dictionary.c
index e1cb33c45a72..8fc369e70623 100644
--- a/dlls/scrrun/tests/dictionary.c
+++ b/dlls/scrrun/tests/dictionary.c
@@ -326,6 +326,90 @@ static IUnknown test_unk = { &test_unk_vtbl };
 static IUnknown test_unk2 = { &test_unk_no_vtbl };
 static IDispatch test_disp = { &test_disp_vtbl };
 
+/* A dispatch object whose QueryInterface(IID_IUnknown) can be made to fail,
+ * modelling a COM object that has been logically destroyed while still
+ * referenced as a dictionary key. */
+struct degen_disp
+{
+    IDispatch IDispatch_iface;
+    BOOL qi_unknown_fails;
+};
+
+static HRESULT WINAPI degen_QI(IDispatch *iface, REFIID riid, void **obj)
+{
+    struct degen_disp *d = CONTAINING_RECORD(iface, struct degen_disp, IDispatch_iface);
+
+    if (IsEqualIID(riid, &IID_IDispatch)) {
+        *obj = iface;
+        return S_OK;
+    }
+    if (IsEqualIID(riid, &IID_IUnknown)) {
+        if (d->qi_unknown_fails) {
+            *obj = NULL;
+            return E_NOINTERFACE;
+        }
+        *obj = iface;
+        return S_OK;
+    }
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI degen_AddRef(IDispatch *iface) { return 2; }
+static ULONG WINAPI degen_Release(IDispatch *iface) { return 1; }
+
+static const IDispatchVtbl degen_disp_vtbl = {
+    degen_QI,
+    degen_AddRef,
+    degen_Release,
+    test_disp_GetTypeInfoCount,
+    test_disp_GetTypeInfo,
+    test_disp_GetIDsOfNames,
+    test_disp_Invoke
+};
+
+static void test_object_key_hashfail(void)
+{
+    struct degen_disp obj = { { &degen_disp_vtbl }, FALSE };
+    VARIANT_BOOL exists;
+    IDictionary *dict;
+    VARIANT key, item;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+            &IID_IDictionary, (void**)&dict);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+
+    V_VT(&key) = VT_DISPATCH;
+    V_DISPATCH(&key) = &obj.IDispatch_iface;
+    V_VT(&item) = VT_I4;
+    V_I4(&item) = 42;
+    hr = IDictionary_Add(dict, &key, &item);
+    ok(hr == S_OK, "Add: %#lx.\n", hr);
+
+    exists = VARIANT_FALSE;
+    hr = IDictionary_Exists(dict, &key, &exists);
+    ok(hr == S_OK, "Exists (live): %#lx.\n", hr);
+    ok(exists == VARIANT_TRUE, "expected the key to exist\n");
+
+    /* The object is now degenerate: hashing the key fails. Exists and Remove
+     * must surface that failure rather than report the key as absent. */
+    obj.qi_unknown_fails = TRUE;
+
+    hr = IDictionary_Exists(dict, &key, &exists);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL, "Exists (degenerate): %#lx.\n", hr);
+
+    hr = IDictionary_Remove(dict, &key);
+    ok(hr == CTL_E_ILLEGALFUNCTIONCALL, "Remove (degenerate): %#lx.\n", hr);
+
+    /* Restore the object and confirm the key is still present and removable. */
+    obj.qi_unknown_fails = FALSE;
+    hr = IDictionary_Remove(dict, &key);
+    ok(hr == S_OK, "Remove (restored): %#lx.\n", hr);
+
+    IDictionary_Release(dict);
+}
+
 static void test_hash_value(void)
 {
     /* string test data */
@@ -1230,6 +1314,7 @@ START_TEST(dictionary)
     test_Remove();
     test_Item();
     test_Add();
+    test_object_key_hashfail();
     test_IEnumVARIANT();
     test_putref_Item();
 
diff --git a/dlls/sechost/security.c b/dlls/sechost/security.c
index 90e227b29483..9f5370a9f53a 100644
--- a/dlls/sechost/security.c
+++ b/dlls/sechost/security.c
@@ -1034,6 +1034,9 @@ static BOOL parse_acl( const WCHAR *string, DWORD *flags, ACL *acl, DWORD *ret_s
             goto err;
         string++;
 
+        while (*string == ' ')
+            string++;
+
         acesize = sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + sidlen;
         length += acesize;
         if (ace)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c
index 261d0606244c..19eda9e00261 100644
--- a/dlls/secur32/schannel.c
+++ b/dlls/secur32/schannel.c
@@ -648,7 +648,7 @@ static BYTE *get_key_blob_ncrypt(const CERT_CONTEXT *ctx, DWORD *size)
     if (keyctx.dwKeySpec != CERT_NCRYPT_KEY_SPEC)
         return NULL;
 
-    key = keyctx.hCryptProv;
+    key = keyctx.hNCryptKey;
 
     status = NCryptExportKey(key, 0, BCRYPT_RSAFULLPRIVATE_BLOB, NULL, NULL, 0, &blob_size, 0);
     if (status)
diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c
index f6bfcc483b06..8f36ead4ef38 100644
--- a/dlls/setupapi/devinst.c
+++ b/dlls/setupapi/devinst.c
@@ -5300,7 +5300,7 @@ BOOL WINAPI SetupCopyOEMInfA( PCSTR source, PCSTR location,
 
     if (required_size) *required_size = size;
 
-    if (dest)
+    if (dest && (ret || GetLastError() == ERROR_FILE_EXISTS))
     {
         if (buffer_size >= size)
         {
diff --git a/dlls/setupapi/setupapi.spec b/dlls/setupapi/setupapi.spec
index 908019b61034..bb0bdb3497d0 100644
--- a/dlls/setupapi/setupapi.spec
+++ b/dlls/setupapi/setupapi.spec
@@ -120,7 +120,7 @@
 @ stub CM_Get_Next_Res_Des
 @ stub CM_Get_Next_Res_Des_Ex
 @ stdcall CM_Get_Parent(ptr long long) cfgmgr32.CM_Get_Parent
-@ stub CM_Get_Parent_Ex
+@ stdcall CM_Get_Parent_Ex(ptr long long ptr) cfgmgr32.CM_Get_Parent_Ex
 @ stub CM_Get_Res_Des_Data
 @ stub CM_Get_Res_Des_Data_Ex
 @ stub CM_Get_Res_Des_Data_Size
diff --git a/dlls/setupapi/tests/devinst.c b/dlls/setupapi/tests/devinst.c
index 605b0257667e..d21a78b36ad5 100644
--- a/dlls/setupapi/tests/devinst.c
+++ b/dlls/setupapi/tests/devinst.c
@@ -2853,6 +2853,12 @@ static void test_devnode(void)
     ok(!ret, "got %#lx\n", ret);
     ok(!strcmp(buffer, "ROOT\\LEGACY_BOGUS\\0000"), "got %s\n", buffer);
 
+    /* CM_Get_Parent parameter validation. The CR_SUCCESS path requires a
+     * PnP-managed device with DEVPKEY_Device_Parent populated by the bus
+     * driver, which is exercised in ntoskrnl/tests/ntoskrnl.c::test_pnp_devices. */
+    ret = CM_Get_Parent(NULL, device.DevInst, 0);
+    ok(ret == CR_INVALID_POINTER, "got %#lx\n", ret);
+
     SetupDiDestroyDeviceInfoList(set);
 }
 
@@ -4648,6 +4654,13 @@ static void test_copy_oem_inf(struct testsign_context *ctx)
     ok(!ret, "Got %d.\n", ret);
     ok(GetLastError() == ERROR_FILE_NOT_FOUND, "Got error %#lx.\n", GetLastError());
 
+    /* try a relative nonexistent SourceInfFileName, with dest parameter */
+    memset(dest, 0xcc, sizeof(dest));
+    SetLastError(0xdeadbeef);
+    ret = SetupCopyOEMInfA("nonexistent", NULL, 0, SP_COPY_NOOVERWRITE, dest, sizeof(dest), NULL, NULL);
+    ok(!ret, "Got %d.\n", ret);
+    ok(GetLastError() == ERROR_FILE_NOT_FOUND, "Got error %#lx.\n", GetLastError());
+
     /* try an absolute nonexistent SourceInfFileName */
     GetCurrentDirectoryA(sizeof(path), path);
     strcat(path, "\\nonexistent");
diff --git a/dlls/sfc/Makefile.in b/dlls/sfc/Makefile.in
index f4e3c7cef97c..f0a0f7c34419 100644
--- a/dlls/sfc/Makefile.in
+++ b/dlls/sfc/Makefile.in
@@ -1,7 +1,7 @@
 MODULE    = sfc.dll
 IMPORTLIB = sfc
 
-EXTRADLLFLAGS = -Wb,--data-only -Wb,--prefer-native
+EXTRADLLFLAGS = -Wb,--data-only
 
 SOURCES = \
 	sfc.spec
diff --git a/dlls/sfc_os/Makefile.in b/dlls/sfc_os/Makefile.in
index a9812a261cd4..596b8823b42e 100644
--- a/dlls/sfc_os/Makefile.in
+++ b/dlls/sfc_os/Makefile.in
@@ -1,7 +1,5 @@
 MODULE    = sfc_os.dll
 IMPORTLIB = sfc_os
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	sfc_os.c
diff --git a/dlls/shell32/Makefile.in b/dlls/shell32/Makefile.in
index 54059b73dc3f..84615e29e3e1 100644
--- a/dlls/shell32/Makefile.in
+++ b/dlls/shell32/Makefile.in
@@ -45,6 +45,7 @@ SOURCES = \
 	resources/folder_open.svg \
 	resources/help.svg \
 	resources/image.svg \
+	resources/info.svg \
 	resources/internet.svg \
 	resources/log_off.svg \
 	resources/menu.svg \
diff --git a/dlls/shell32/iconcache.c b/dlls/shell32/iconcache.c
index 9433361440c7..873f1a12a123 100644
--- a/dlls/shell32/iconcache.c
+++ b/dlls/shell32/iconcache.c
@@ -1037,7 +1037,7 @@ static INT stock_icon_id_to_resource_id(SHSTOCKICONID id)
     MISSING_ICON(SIID_FOLDERFRONT);
     case SIID_SHIELD:             return IDI_SHELL_SHIELD;
     MISSING_ICON(SIID_WARNING);
-    MISSING_ICON(SIID_INFO);
+    case SIID_INFO:               return IDI_SHELL_INFO;
     case SIID_ERROR:              return IDI_SHELL_RESTRICTED;
     MISSING_ICON(SIID_KEY);
     MISSING_ICON(SIID_SOFTWARE);
diff --git a/dlls/shell32/resources/info.ico b/dlls/shell32/resources/info.ico
new file mode 100644
index 0000000000000000000000000000000000000000..69d9e00d89338eebee3b8e41634c3d993138ac66
GIT binary patch
literal 47823
zcmeFa2RznY_&9v52uY<<DXT<DqM;-sWJcLSX0+@rTUKV7*<_c!vQ@N~p0=d4RTM>{
z?|Yq_TQ`lT=lMOq&-;1*@Bj3<oOzw=y3V-Hxz0J?JA|kZBVuJ`1)mo2Qz4Xt5X2G0
z8ITnnLf0T|EY5|jXc1aZj}RBvSov4f2&FRN_*nTo2804x$Z;-|2lWWeM@U$Bti07U
zgzhZI@$vFdc8d@>4&|32^kOqG0FDZAA!%KNB1{qDhd5LPDL5UN0Ov#cJN5_Xk)5}<
zHzEiEc|$^Tb2B;lANo|YX5mVy=7@@}fdF_Az`n><@To*aMIeC{{Iyg4tz+pJ0q2Y2
zIN*U#!iN;_$@QXfI^*08aU#?O?b}QP@<F>}_?08)<FXhB<4$fT=nU*A>lhDL2ks_i
zh3{C73hm|4)$&vnt1F1|t))<1m?dgFYJ;i*3{ic=K2%`46=m;PkD6laQH75#stvV3
z)q%#SA?hGHs=ErcH=IPrz>fjQx0Xh?&sLzOqqgY&#d=iiz5^xglS9GE+~}0E3X0sx
zhoS&Z9F#-Ht+t|AqxI;hrU1&dl|y*|MeaLM@;-S~V6T9>AKpeuR<h{T%jYP5*J>2H
zofpOGtwNRlyHJj`EIMJa78Te?qpW?B=yZq~l$Ap#AY66W2vq|V+R35{Up-WNWDkne
z;6vAIvQdG<HdN-RiB5uF6J&xKqpeZCjSQ;tH-Nfi&`BpHRPL<<^_iolV|FN9eL0HJ
z5rjG{QIy7VlzLDR6}zdUGiiP(S_fn{T!&&Y=&yo2B~%@<2SsbGL~q}}N7)Bu&`}*B
zbkao)oj3rz?O6wHDT9jKc7l9zr1t;6fBtU^{J+rxRM@T9!MS<I3Q`dMtP_&ogv7RO
zp<72v*{Qcyj(N=*!)dbXHE^!*YKURSEGfE(ib|6nqQ)yFaKdzDsugM*A!MYx7?%*?
zU!lL2R9ZkA$C+7lcxU0jnmOtuk9CQv3=S+@Kud~CtLli4*N)@Nt9LBolft2S^YLG%
z#oSc06>)*B0`P}fT#$-tJJ-x{4CW2XFvmOwGo&DenTByG4@rRSY`az185DNSose)j
zHODF?EmG!xt7o~!x^Qe+`fYb6=nJZY{<|5mV_84=MC8t8qvPS5kf-)LWGH_F^dA8_
ziU7SvfDR`>&k>-n2+(T;=x+j-`B@!6;|1s=0`w#SdX4~nPJsR#gFWjA(E9}FKmzn9
z5ea1}>%et_z9m5KPR1kSkNdbz(5(dMV<Jj}?>epn%L}@ZfHh?jF6ci3^g01L`8yv_
z53U2&9RhSNf!hOU-voV2fG(d@4)iDiI+lPnWl}np3DyJx)(_kkH$j$<SmsIcj`Iua
z3jzB22Yg%>);S_Ved$27#_|zZ=YA-I>A<>2z&h~LJXjxbJB|Vmqp-&OPzKgT0@g<Y
z*4-cSaNV#rVg5%k53qjyl>dqT8<&S1$Nb~IB8@BP2Ut@HSnqzI18X$_YxNK5u-=o}
z71rwZ#9gbDVT~tXE&PE8ST_mWhGe?f$FwoOuwE1KdMkJ0F>q9K`6XEI3D`IMP{&kU
zSi1?>2mCOOp)H@o8cM((gd_`Pf8sFgVF=i(5U`KH?fx9oo$4?juumai{RJK^3D~0$
zu+M=xVNSprO~C$zG-jsaQN|N@393eG@LfByoo5L4G(@N>&$lD0JVQ|TMaWa3EQ`bc
zg`VMpdZYlz`(6ZlZnFFD_r2tr|HPM-m6iHlj#hTotXXWb<OEu2h=L<0KooC%XU!Tz
zp`8U=V(ezIkuaojE{`lOK`J0U2e#HrNd<767H6;J#ql*5VJuF{{~j0p9>?gSV{uVj
z;aFS-C;pQ-?A<BKi||a2i>{rBllZ}fDf#;`4)TrhKh6*j#K+~u0*XTZxcpPw2Tf@|
zG_`%vl=g>4n%qB-@Jk2w(wOVxBnTfG;u}K52twXN2ub!K#My<=ObpEk(UZXkA(~$S
zxeWHZ#&K~yxISDjt{>AOFg<YCZs7uu0>E}B$x*JS6gEtAtOH5~n<8iLMv{$jCfOV8
z4oP$l{E8q8l;WU+(g4n897b*Fez2D}MQN^vB)g^Nh!r~JsDScZcA)dQ5vU<@KRRwM
zfePJpP>I|2G21295j6v}0F;3(Pyul7LM_VOy9q^TuSRE&yQ6ae*glHV7eQx}JV|y_
zioF)93N%C6_A00?%?IUzUlY0qYz=vo?y?K)1~rsqz6I#0p(pp-Q7G6JVLMi!axX2E
zV<m>JmL{Q!03(!la0}G$f%3t>UX=lL`J!5|N3hLO;G%)*BM+itS5*{e0rtuvJ+Nmq
zP;-m}DhEH?ay?1|TO;G30@z6gsQj=gYD@J-tzfUbe)Sxk1iRzd?oFu3MVVxyblts%
zj+;q9U0cyfcTH5{x*dh6@u3R^(Wn{hA8adiKe`KbYeL-{P>GKrs`S%Gc?TrGW->(A
zD$`N69mt#NgIZ67pf-RDS%Ii6I}G)_dyDGB_W~^yR1EM}b1{mykOewgsPxcIR0eR$
zdlzbqaRU2t9g5Hd+s<zn)V&2o>WQLrDPAbsRtdF&UtlYZPS~rX)~q0?_Z=#7Rze}$
zm!U#;ZFGE(B-n8WQ3KeVjR4s8EIp)w8jm>u4Gk2gu@Z#?w8T53mIOES{7DDO-M<;-
z0YvJq0UF9E%WgZ+@kXhRFm?bsZnUDKMw`(23_qY5jOxRzfTl9K)l`J)BCOF#H%*kZ
zM+)fKp=9t&J$8`n(IYzrU~FzhEpd)0ZNEI&%P@X?^?{BEI+x^*PS_~IIQ4{aVvNqD
z`lA}~v3*?`yayG6pX;E4Zk)~`*{n5Td(kN;Mbz{5Eh+;4R#P!LvU3Fr1BlRAg_`4C
zN%nZDpAm}C5r%QEO|pkm!LJH32RZa${QIIBuw73%ZX?;Yai)?eM1==6g6$q>DThkj
z)ln(H{}0{8i%HEl!;=y&{#14$(QKVd(hH?8>16A{8Pybbv89Hp{p7W2oP5_#2FkBo
zJEacWxxq8}a>?#O?Fr#qlob>mqLL(MlKlFDlA`YgwjK&`8_VX~Jzh*aBzB^Pi*pxk
z97|?uP+PL^C@Cynphe~(?*zGWS`Gc{b*5uw>x1&fC|nfSGG1wJlg3qYFgJIm1subH
z)06k2Q5Z{}qha|ypN~ouXJ5=JUbk-DnhVa5!>W<~JwG?_Fy`RA$}R(N>%vyi<W_2k
zP~@M>Oo05PvojpPg@uJVROcG{j&YAmIp5~r0r}zEU3188C%cB&cm?a#b9fiHK>p3W
zM%W}=^A~m-G|EZ&oRYV5au$2|Lw>WzNg3?sV`rsdGi^Q1u~{SplPHXzbi~Y><YZ*Z
zqBS%$Nrg!W6&aZR^40@>B$t+!c2F?IJS}akxV1H#G3NgwHBbGRYaDlD#r^y|^GQ9R
zx5r_;z+`XR9#Z?Ei?bJg&se?Lha8Et8*6`*$9;;NR!^s~KXfdjTp9t?vAdWpkwY%P
zas2dH&>>lo-2Z5+l;=c&@q+fM^Crd*LPY^rzLUTuPF)FbAFF)q?^dPwh2N!+TS#B1
zO_>cm#5hXuq@I$~l`~1*v_kgG1nr4piEZ9hPRp2=IWfd>U7kp#3|wTqQFLbMjp;w)
zQkI$$CT$k*pRSpIv`_K*%^roE*F3(b>H6J11=9mJd7r|CNRD;P`xk%V<d(GAnF0PR
zfVlwd01E)%)EwcRE>6e!cvD2N^M}|!cXWV)>*D}e27rg$27oO9c%QBafNdBuC_s2C
zfCRugfK>oX0Tu#asc8ZJ#Npi=3joIB0}uhgG?mzA(3$KIWJ_|C<hbP}&-o@mfom{O
ziEH$*BG+ecdCu1kQXK6&1ZOAAok3>|<uQMlmo)&Gf80*Y0DtFxpub@;v#y)W;tN5_
zJi~FjHx8Zh&?2gW%!vBP14J{}nApd0oF2D({SYqawslFH$P#8e75*uGGJka3^O)v)
zZ(EWUtjaqG`@<2~%MuMh3-*8n>`O`KFuD80Nj{F3dux;On_?V^99!j)U}c^`ANeK6
z7tSD!jX!OF68%8MrE^1+xvnM{Z~P4V7LwhYe^7Fg{Z46*Q-*OlY`-@~+Yt%I8$O39
zb6*VHv1kriu)p&$?I30D7bk4C4a2?~Y<T%UqD#pG4&ye#_?ZXghk=LgKj9%!RFp1Q
ziThHvt-=><^TYn~U(qJ>0{e2(*+CYG2d=9=S}UjhPJZaKxI}1k%=hnTpGoy4-n@BD
zyy$*R+`rU7G$(rz*?Twrlvmh)6L{<-nr!?OtjtAT8-8FTNO=iQ$adaOjc|VRcWwUg
zN;Bz9?OH`TQ59@XoJ|iP^5HCvEca9&_eDdrEdl$<&zQ~+#32FBNGft2R7da`+*JIj
z@!KtBB>I^4)VyEC@tDl9Rr(6!`qWhVcx;3!^A3S6M>-S4^9pR3U)Fu0@F<D?ug>&-
z#sfY##d9-Q1<r$Hd1g}b5Uj{;lx)7a|6Gy>X@285Uhbt$fUQ8Bw3jEqex1ZiU6c)p
zKJ5P|rT>gBWg0%8Of(Vu2;)F^5`7h(DzJx1V-=svVkq~-Jd6R3gMBf<$4Lm|Gu6kp
zF8w3=SVtAQYvOTGOi3T&4_gx5iJD+j0&Kl;&;UNB0OaBGJA97!;%O&o4oqzuN_i>6
zxE!v#GSG~Gd3JjW{odAeKLYGkQk&y;FNJegl8?hv0BmaF9-PVIHYkNNbxJx@!(gwI
zbP<*XpTRfBIfMLMZz<^qD{+ln$O?k?)+9>Y)y4qMvccy1C4jw8qTih8@l*O>-@{qB
z+$7$?#w5VTCqTyyQ_=^0`vGic0?sc8EE@)}?+G~P_$8c$^K2}CdtE;0Dhbkj!8E~E
zCXLxK+{ye`!CCW}1UG1Zu6LC5LzQ_t8e<$t_X=SD6W?Ks7cx)SuR6S&fU^kVP0t$=
z{Z1Hzl=+n5sWN~KP1HqN!yM$kJDEOrHJpFI-GdbY=LHm?Ou+fa4^R%{q&evjajiU!
zxZ74k!7nB6I6RsE`pEr6nXeI?1#_2B(uaMFNvf4}|Jmbiq<Mzt8ipcA0POz~D0%%w
zn1cV-WKSa5VoQIp5~n^T{V-KNwosL&LvVfs<7MY09>~1>Tc0cgoUahAux?@f=c5jf
zLZ&#F6ICZYbVl%=>Q8CmzP?y^jMNu@hl6!{p_?Y`izc@{nT<dt9==eurJvy}3GQ_C
z{)mTj8HY(@5YCy1GPno&6%UoZ`ouX{qeE4fehyV!%uA*@)rWmbVyczY=kuV8{-hsj
zU`>1Z{0Y(d*Vr8R8GWp~;B1FTwUYT9s?2$GDox5b-v5NEa9=5K(;CM50OS6w&Oc4V
zGPI?569ukX!@(+C*Zyw*gLyf;oqK+$3eU?z5A9*h_n-E~RG#p?CvHbLuN@9i<$jLG
zIHjz=3u7LjFRrH_*f!V-`Vx=HKj{z5172hBehcHJ?~@yZaeWo<X@5uWXL&~zm(t_$
z5TUv9Q;GlX5jdYFuzsk6J2NaBo=?S~qp^?UI33QKp|AajVlRV{aLtu??;ZOmZT>U*
z<TN}l;NGe#Qe)LnmaWRCs!%JUG1i$l3;M4u19$?zG1iHw47Mb)Y!n9~Gz5p>uB$v~
z8!xv1{xgUBFGz{YI81GMO{B)kjtDKm4-pzGN5Xar^o8%_e;BfTX*J$Q{mDN6KV#}Y
zsEBk}fdBmAIYl#^LHPXUtODngq3d_&6cqO<!U!Q_0P?+|4~4Zzsi^;(_i{k!f78*z
zT^8kK<2Wr@(k0{fOGk1t>E@B*@^LI3Ur>%aSqf%paT>mT!@kt_45)h!PR4g~<F}MJ
z6N=9vrH*AxAV_~t#u#M11jTXbwX&qULZC5@FkW1QhiB~8l8ivQ2!-c`q*Op0Pam&_
zB7LkP@;%{1dGh_~I5#5riV?eUj>fAOT}w_MC-94OGFiBr9pfLj5=cNMOOZ|@Op(qz
zMtAH&^Lx6?KTMx2*!OyVkuE#doFsuM`IO}$3p~yzsaG(I_)Zo(l3Z}jIu`r>cdP?`
zoj&J#Q5+g?=kJV;XOqa1M$dS{*yZuWfS4d4GuHFp$HSa4gt7FAfi=-H<CoCnaRm~L
zBVe?N;=dlZ_-1(=W70Us1O2D-0?FcxF-JL%;0*ES$&m5$c?wMZd|snJbYprJUnXNm
z>37kO#c>({Tsli)zYmlr_Pao#V!s)bF7|yuPhua_g+FjV4Y$+?0U98}I}Yq~e)pjo
zs0bml--YAw26w98jMHi0TH-(QyKp@}p+n|_>`&#FEC*RG5RoM3gxt;J?a(#eE>IDv
zoyhG52xIL@Zr8E)#^>R}0LB0l_x95vLUt7Q_9dQrD1F~%RF`lFo$@h7;W}$jf(}3G
zxON6r!5#f^-IXZ8KnUH0ce+<=3sFVbeiQ?D^Kahwz@4iV%JtAhDJE-Cuqq#_Jm!Mh
z@}g0;mm$jYFhr$rci#Z_^_hD%p(rh0c&ob_mBRgeg|8th3fhC3l6_D~kPW;e7DPR-
zy3x6eK$L34i(0b6P*-~!iZ>RAcL3{9O_UAXt*f9!D``~dt&55e!F%U;4|LMo1YIgi
zMVb4yp%d`NIbM4?y!l-L_qHZ*FRhARyncyN47pLJlQyaiw?z4HPoHcoiYkseqt@Ig
zl<RJQGAuTtNKGCTswD*PpoLJ@LAbY0@<wr5%TO-7Th4>K`8O}0qUOvaC=TxElXkB`
z)sfbuyZXm)PoH2Z1@8x>P?3iYD)cf&%?U2(n6?OdaHSdEJs87VgmtJibT2v`tPk%7
zvfz!fKT0;>LX|;#Q3|}DE{}FXIc~d9_91fwXKkqNm_1527en`2D^U^LyO+UTd?wtz
zpFC`i8jgFRSk0yIu6sF3woxbD$LEGRprl<(P%gZy?!0jhCBvKNTqkvO+(;O`e)|Sh
zMI3}T-b(PEKoX^ytw*QeZoVeg4HbCqM&%yrs0r@hPgre6>F`ebsMZ=(4sXzp>vO;x
zf=tws>Id(ol~KBbCfrwBz+JThIuG~m*{=F1##A2F!ae(6&86rR+@+U#>7)8YPgEFS
zhKhX6P!8OiC+P{IM8nl6$59Qn!~J<>_yKgnMHfA~eFff2>_v&DAeS4w&xmn_cLU4O
z(+BM+&)X86FHS;Pmf|SQQWkZ0Jw(x(!sy<mMpOpx$1CAp{loz!R2*!Hu2y8A<`iFe
zPhpALGJ{as?xm<Q%mF2sY(_o3la4*eBme*U|KDl>yU<9wIdkYDL+$>6?|HH+n`re-
zXlP9Ivp1>8&ig&e4t^;%llgKx83O_sJ@!eNY%x{Y@oNklTiVRcRoh6epx_`lRcAJD
zTbo~C$jtKEylySHfH9P97)5(?W`xX75v*sNWSll_8b$y&jss+P;<)kc;1p+8#kP4m
z6vxhb3d-><Rc2F)BNnfH)3F=FAft?vq0X$DQl2hTE-;XU@EvE23JHaJ%Vd_eR&EX?
zxiJ*w6Y6V{^C&nAaGpUaw9Wj(_V)JE$4~;N$5&TZyXIG`kMTA}X0a(nv)Zm!O#lQk
z4qJ8giP_Wl9Ho?o(aUy11i?5BBO{}P2^8Jx>Yxc}$PKWWcA}^GX<VED0ppN}Y^bg-
zq>yHe438IHQn8!ot+8?AMkl8+93W%FTdl-1F3tBg+dO}K@GbIMXgqtN^r6{_<0wWt
z)z$H2VSvyW&g{(bEgQd=L=oO0ipu5A1~R}_PIdK+327!ciyC9d&eTewXbeGAbl;&w
zU@fY;dM;%Hj5V}(3TXm`mU+g<#}|=NL}qW?=v-ZGNs@oO0mix}J~kAK%i^XM5&{?#
zD7h{bV%7PS4FIwv&Q8di5sAywc`3%nhk%>Fkvl|RT}=&bHYN>FQq2<)p`M*d8bn#8
zq>__R463US8yjN`jN+0jCnqN&;&LDh*Pj&}?|`euZk$TIVRg0gvSrIiD5TPWAUAug
zJl$$V{IG?LL*ndkb+!2+Kv?E-$Zx!@<>dUmNc^yD&mfimj>5d07e-vXoPvS^sb9wb
z#3$pMa}>%+r3h{u2Xj^8C{|rPGwgc<j3FG~Pijv)sr9%#xbI{Xck?AtO=>ehiHjIh
zcvV_s#V4a+Lf;zzx>JrkKo`ZV{Eje*3>k&QnS<9N%sz}~+QXB||A<0r=cAfrWy&u<
zwLHW%j*W>!qJjU=kmm~$JY4nzLb>ZqNs0!DkB?g{IK~eA)#Gvxn}U&AU7b%#8gDek
z1K;O6vJp;$cT1B{n5wIz$yHz!w^bDS1aYijzz7s5YD>e_$mPM;U9ok78yv7bu*BVc
z3}q6Tag@C=ll37&0*0sD!TlU%FBfU#P*N}x;1b6O;C{e4BG2-JJ_nYfeA&kkCZnue
z5Jk~66f`)FhNr2KG00ThS<8hvC<;*qZS)wz)2JpefQ(RDc(ER3LsAxpups}*rQsI4
z_WHQH`|RCi5x(>!|My;<Qg$plk0br48_m+Wl1qKvR#kD3huBZcjwjlMve0E^(Xsr|
ze&BZ%;XIOgO0uE;hjuEdGLq>=`a`zo$oAb-yRQ$IpZR})ol2Iw`M<DJxo~Y`FPX+d
z|K(q7@zMa`smcHVUq_SPZ{fMf3NULNpjSw5qVW4AY%$|EB(wk&7Bj^DcO8CDhIz!i
z;PXoSJ_}Ac5HG-TfRzCF&DUyxH30bCmgqR(urPoS04|61@KOLS0Ic%y9K*6Q0${oR
zD~H<$w->IP9RTyo2Y@vI=4~SYep`mOIf?+P0O|l509q7)<2wMh11JH=0Z0Le1K_uA
zSS~CRHvn#zIRLn?aNqu0hsOjS2e@sP0ARi`o%H~iXL$fs76uvvIlfuJ=3?yCE;1bV
zeYbJ;1t@ZT2~y%5#el=U@|=Cn(i{)XHq5V;S;`tQgMr2zaJ2!{02BbE0K@=rzX<?v
z0pRy?xZQA{|5JzC9rq<}2i(7SjNpD>3$PhFSWacdY;PCo#a99qxrUEutr|?;r!bo9
zu16Ffwj?UTY>3JzC!zwN9KuDAmIHao`?rlA(G(oSI8IU=*A@6@1wsSk{@MnB+XS}_
zZX?`QivjSR+YEqzGp2sp{sy9RUHrCjJ`d9n{G8>m^IK(<BT;?Kl{n>VLY%PQ4tK25
z_?;CIZ@88O@YaS%wUi>V9JUjMKE^~9l&y$xATk|xe8Y9%x^#r+dH|3AW&1;QBsVes
zb%bV%`73f>3D*?-obP2s)Wmuag`T^JBy(}%m=^y};V7h&Kok54SWYCFZ6fjx?INmU
z+=)DoT?BkD@yTZ!=T%jHR!N}xUyc895nwl~#H9y!_5&IAYNIuAK+{bdz8MrIVzdP&
z!7+#*)57o@c_!rGCt~%4h+J1~ki(5gw^1F1H!uT-w@rQ*F~P*Y2*46f6{N%+8m6&g
zpd@%7Q5v$JIBvY2h}BsEAV7ktGEm0H<oSD95{%XorBHuSpcMgkp#wq6T*3c>-AO2z
zcet-ljn-ZBsrr};kq>Pir?+yPcg#N}jLQNtQSwh2{##j~?{ZzVh>A!jB1&5rzf(+^
z#Lz#C1S|4{MC*$T)Wo_IC+w7oINcRLz+@RF%M<e(S+HEck|)zf5$3oX5v{Z4)4%P9
zU{y|ixYz!48pdU|of7^wb_{e^f(&H%Q+fXRILokBBr2nw3Cu&V!sK^?f7cE8&L7_L
z4OE0#6Yz%QJMUx}$o_;JSf1~))PT&l+iHnhXRC<wr;Zbq5%xdMncwscSqJ2}Xb>gA
z`{At-Z~yP!+f0=Q#$9WUi`Hm)@Lv2)`F9}m{R<!WOP-4+=`9d`6GsVeUcVx)Hxx~o
zGk>E4V1AVZ?IkiDHAX@Aot?_(&*Fi~oI4|Rg$L^5J%|*Obwna1|C7QiiG-iXQFGLJ
zg6}8rt#*#1I+0?&iO6-{Nz^5JPxRBz=gp5gAi-b_f%`K`XY~NA*?67)r6cJZW$uS1
zhb@SFR}BKbB?k~31K|DFvOph=%TgAycY^=R<rx!W@1M_^vA)4NAjc8j-};%udr7Vi
z%;T>F2v+1&Jf<hoU!QP@NH!8Cj+6MuGLT_h7A(`GzW6@Ia-DZf@c-A@$_f4_=0BG6
z7xRWZho<&TJp7&l+^Y~rb=UO6+Znvq`>BJqucFY);M>VVI>d2<Rg(c^m@Lm^S;+Dn
zH(EpBv5v=n_u~g+bLS`Hj8dLI=^MP>=eg^DgSE8$XUzLZtz^dENYo{I5^3gQM6#hU
z0e4D7`ksvdn@DMJ7f2)-t|rMbSr&?M27S|U^TI?Qq*+RQpZ~be{-rEQ#_NdFaUNI~
z4f?1q#QWk&4);rfiTKCrD0?Cm_{6`xk!`(o3=V=1a01dWWLilOsitd*<Ay>%mF03}
z<^=yG{-(biYYF;4*8vlKGi97rMA#CsyElFeQR2q7&m;$9CoI=Zd$ibl7m))z<^b;)
zCS)Lo$7R9tW$l+DQlXDXa*)RxjI+A<LlgWrrw9LI9WcTFl)ftT&?8PbX^z4>+mK1j
zLj>N)ohb!<U+B6M_?8`qF&X|&mJ|D>p+DA5%$Gv=28gWtZ#0~unE#W<8F|i3T4Tt1
z0eGJ}*0QX(5k&zO_}z5VRQ_R~_PjR6mB@EgCUSv)4BzD-hri47L%%@(WbNHTr0iZp
zq!^1%ysL)q&L`x;b7u0q`MD0Dln3jB6jSJ9gVnH(Y$U3poe0<yJ)6ouq<v~i_9Jp_
zfOj&C%kgu0zRNOcoWZ!lbN<n-%VT{2Z*tQt#J<ZwG0rB-GP!S(cZ(3|Fwe5~<8cae
zZl4r!9Mb9%ynuhs{;B+jDD#XoryVBpfcHEbITB3Bf#vy!vfQXIoESqzJ_baRfe;CP
z(92jJihfCg_RavEm3094Cd0T4Nx*+&vOn<8^K~l!u)i2-N)3Rv-%9#D6|xLuKNn>B
zl{`NiXJ=0wnc%-E#Sio@@K1(uS(3<o0e+IvYT#RZOveG=C&;{0_*j4ciht7hN%AG~
zZRG*vNPvGAW&+0h<hwk-A7@pe-}ezW>rWD?#%o|rp^#xbjCFOU<z^xq^9*C}2cVE8
zZI3unpZN3f1MU9;?;8pnl*VQFE(expLJl1MjVvu`0mRj+ETZFj>qPrMzI%hD3r>K(
z&9d4I{ECz2I&R}1^Z<o^o+?Wg%z+v>y8!+3!~6|V;XYGw<N#6Ru0a&o0q>MR(I<Z=
zOUI42iTwjvhxWYfA@c1Nf1=ZVp#y%h)?it39hAZT-uF}eiPx_JPyNyI0AmvWSO!X%
zkcTA8@B1d-P66IQ!#nJ(BSd>+5%CD#<(K*z{$m|5wQq{N^x>>R2i7m1AJ#9t_e`;p
z{a71m1K&n%gWt2lFj)q&JpV)%%CYu49q{9NGr4c@SgQ;>KpfvA^V9tU-oJ%zUpfeH
zW}w}-kz~Mf{GBY5#@ZkB&189guLHj8HQ2`h{|(2zi4e7=Ki$7!eG1<cR+a@@eJh1`
z_=WKMS!DjF`u{t9Lm|tQ^#;pv(nFgl4z&2?{u%R+$55h~)IejLEATEqCIgn^e<(|V
zgCf*{_1c(T{yQCj`)Kl7vz4feaUl{+w+#Hwe!;dG{)Vt7+JPwb(gB(Z6fiEw-^w$2
z&NL>tlYVy&+cYhy{$qOWAC9wA9-2gTq#Xg{<G1!B?gKcl*GROG{L}>BLE`UaPXZKT
zm?R61Q^+$uPkuJey6)YWSO@TT!awK$tfzmcpCPXy-i=5wllT;*!in!c{?@^_E^Q@&
zmZQ}n79{?$3}pCQS*DD&hgZ)`@c;Pk^$GsTa*+LB>9k7ty}nZcX5WCP*1u(Zya0T6
zv<#o2HXZjO;P;$>cf|?*$#RhW-}Vd0Qtod=+-a*O?p>%Ossc?Xbi$wLfMT#C>f$}|
zdG0?tzrtdKsBrFzHQxBC1@tNWw$a4*Tfdd%e=yFbt~EvQ-E32m4-sp!=~JjGm)@VY
zJ!QEd74Fcaz49P~4^ax=%})KcYfP4(_su^YXOwfN*h7P80$qRHa_eWXbweq6`X^!1
zd6o)K>Tye%&+uNAD2H`nstlCz|Dtb7Jhh3IWM3j_uk2^|#xnJH=T(0q3rPlL?jvy~
z;$WZo61DK#6ZqR$O1^&<{-=F|%byP42m5;;BF=czC*1D;Q{E}%!}9^(QI&=4A3dAu
zLsY<Tm;Q|JpQYjN+<(_M<-WV1&Ao}T;C-X;z1;x5tD>a+Uxmlc)w$Z>cdI`&fUR%_
zzNy5&fB7f<K`9rO<rMrTY$g0|YIBkoQ4eRu$Dz(3(A)p@_j*5*Jy4NbGgOuLQOZ8~
zftq8E-`Y-u67bd=zWX;P%HZ2~coY6(-#{A9tARD83BH|g%?crEjyilx*|%*VM1}Ve
zKKK3^t^chw(!7BE609qg5n3w;Gwsy+%R=^zVx4zBJB(-rnc&?w(V7`J76yB`5zf!y
zjA=9j%3>U_`6}?9>%Sc5|3+TC_s07wYy-hJvMoopFMA%oL*P@Sw$KO$96qAD>^aT@
zo{!+N80X*6`accl*YC8N;m0cB$rXS%_Q~JH^o@Tf(>4Cxj1O#DaXxMs`11|>-rxQI
zQ{QQk<s{2Z_>8wh*FXE7C`5hxwi%41^Yj^t3bLRB@bwIQSu_ni1N*!^;L%!2t4bqO
z8p$+bf**8nnJMgqM?UWGK*T2ip<zh!d4-UZFhXyP5!#dhzoliHT)Sg4WK1(v-X;qj
z1OL(+a*sg@y`!S49Q{*TwwYY~^R6|>LK3T)$#!S>u`O4i%nEV*T83fuv>Z)@bOaP-
zr8Qk2ylZetHrMf7J}j|8v*^v6<XZ#J7AR(gMVZqwOs8vRr&(`QJa~$RW&c;V#ZqU!
zxqdbY&~<M3X593$WPj83@(&N@b#bxMD2Pzg$msEg^Es)xsJ%4U*x}mAc{}4x=Ps?m
z&e~Y_H-*>p3a(2$ak+MCw^)<IwO+KU;)V><kAKcIPRrjwwMvsFhQsTzcW=znYz>RO
z%d;})$j<O*U5)Zq_ythWD>3A~q1)Pyqy!X20+b%c$}+I-$ggd=<S5!5w;{gKeUo8t
zYGs$8K6UzZyZ)=**NR=_Sjw4Md|KGE9XZ&{xY%igY15f*yHzhhg(#6qn8{C1=JtY2
z5m8x-)w{pku_?aoaX`N$Fb-MShIJB&US~u$__X&--&&N*c9%+QnoseDgVS`gnitW!
z&Plf^2&kVmFx{r{Q}!*>hUxoT*jG{AP#Ef)^VZe$W56X57vHBW)b`7z&iANlAHG2C
zIz9dI@|Z8auXcEs45>w0-$}}wXKyv#XOr20XWEkGB7_m^F!FIh3|i$9aoomw)0nxt
z#HN9yTo)Pnv!l!_9~kB!A)li*vu?mo2p@9!eEQb?eH$M5tpQ=%YQDUXd|B<DHK?w{
z*|BGsH}<ddBDB(Ji@b`NjAk+wCk5TT`p(ncwMJ|j$7MeghvKBS_0C7XZWnlTDw1c=
zkj)}5hG!-J1%}xoegTf!@_Z|Kd6h1hTP$s-7V$H99d%-Q-@&=hm@hvqytRAunL$J6
z)ym*Flt;H^gw})0^LYwm>$d5w^wKx>R)#K?w_$Q;WvRI_Z-A@IDMR8J&3O?nW}3kJ
z-^}%dnJ;=SUplz>%KOWFZZ@7lgRegm0#`kAV`Q`hG_~lmSMX<__AWTCtRawBAQfYN
zPJ`N1k?N-Gyi)UdH&6Iz%FCu2tYKmd<)j{%&hj=s_u3)TjLu@~rmSVCAUK+SN!>B-
zXOF3dSbeNlS~OU*q#QJItfs&5@X1!4I!?jM)}k9|B3?F^a_9z9nJ6sD5|1?Wl@iEP
z%GfFOXddfE=?odIxp|>wUj>3i)|)LjX|t@fHnz=uDV<#9(pj2HeKhi|lKjlyoZshf
z<GJmjaaTy6y4i%;tFLtXmLqA?xFu(H)!et=GBeWmJ(s)qp;y*}ylj1b&&5gvnIgJd
z>;!cKeN5O(RJ!ZeGrrYlT#>CFb5NR!QENx~1rbd--=&JWkta%OW6!+V!?=H!K;8@0
z071IOD?TIF6N|l{K1QaGs;pXA!~2!@*@sCE4MnQwanfIir0Po$n8V^Kd~eP<TN?(6
z6e?SenSnrzLoAN!vPc&@waoFJbvsaKfs7VEo7SV-BcTOSu@?Mg%sbdh>r>C2ap&k?
zX=g5Xt1&=1;0CemVrrBA&<pR*0+-!Mi?4pVyl-uX!|_G^9$v{iKW&(PVb$}pa%O37
z9aet{>9B#ymfrqn6(d!FFRK$%OE2~@BIzjZ3oB@MNNa0yU0Ok_7+65fNXI7UoB86!
zum|5ps`D=zPrI_9<~8WDux-C!htRwwslMl1&s-I?eP_NtWU+5L{|fWN*5Zo=%=LT=
z6ryCO7bZ;?JlVRYdiFMvgZmyzh3*{IiQ)EMJNOsxEiJdEy9hd9T0}kJAT6Ck%t~*f
zGOjTA_Bo1vj{jnIPUf>O`+Z&-8h_~+NwG{!o^zf0R<7KF{F7m_`Z7GLxD%tJXPYaO
z?L78vj!c(RIfJVht*6kC#Ad%OEYfO4YR}VmGV1N%zWnC>TB#j#`H#}-9W5z~ZXS3#
zZ%@2g)aBL61#Cl`vIjn#7LD|3;-e{vyfGWivwb$2#uO?t>clw0Cv`Uaqq_k|8~vFC
z_BT>G+mPUO|86(o03S;|+vJU^t1mEC&8Fs=J}WvPSXSOIQ`_@Y=lXgf7H-MNYf+2d
zQEia%+F~;*Ju`Oag)&CP9WL>_4rinSqUH*hv$w{!GH7o&#;3phv3CBj$0}*#C99~6
zscCw}-#G}RrqRFRMefSa-@WeZzNb4s@auyv`_nC_4K|)%T$lS!nS0rVdnydU=jaYB
zqdQSPx48W7TxM>onYA}&4IJ>UZJ9Ug5R8?hS{E**`e?8%ILgm6PtC;MnkoL}dTyoc
zRf`!npB286yQlGU$o!s1g#0#T6w`cl1F^B`{-Q6>?Zea<gzqgrcQM3hz&WV7&cJx%
z5?XY9P1nY%ysBLEbiwq(Xy!XS3(oEQ>hW+hx74;9;f>C#hc|ilu@BARM|#>^&sAq>
z7Rbz#GZ$H)vRc;GWDm`U81(`H#?a!ju;KibYMyTl8ooZt1C7Ze)_23QSIbwDx;4bq
zO)R3NC4+@yaj&cx8|roTx3HJAN;P}(H0<SP#%$dqdthF*=&R)2?`W8H^My-R?xQ0>
zSjK33f6SXx)QlHGnb#z>UU@q2np&}$Xq>?9043k1=jQLL^>gQbvYL)&$S-kyE4K9l
zfoyk|Zr*(U={a8^js-n|k9+BQ?R}$?_x3)$pK0_lJRrFB)6%$Vkr@(^#q<5PEIIqg
zi`jX&>nUTd(u!=>Wpq2g4M=HoP1h4FK5y%6e=0eRi#m#EKmFhvQUCGM`DGO-eHn6&
z?tfe8E#<qL4_U?pansH@`>y7Lf+RH+r-9Jm`$l~%mEI$tJr;CFdiGv9wLHmd%kITD
zQamrzT~M8~nf)D8diA4S!Z|TJgCE}VUz`_8y=JAc-KtQX)j}pKwLc!XDW~*drOx%u
zAB#V|cjE<mC~QmL5KX`hNg83(fn-+WLtJ5tA_lS~pc(CYm*few4He!L+@n4Fo|5^y
zw;_+u^NSr)UqO$WTT9vBe=w_;WN=B<Dlwb&Oq^Lu&KHKKL{r4w)y3S2La%v;jvjgL
z{pF*M_x`L--koX&x2TPuHeMg(PafHZyvlS_8?PbjEq5Y2i-g6hJ?<vLVr4yFr>jGQ
zO7LKgorQp}`5tfa{*%7P_Y9dDv-@>AbMg)QTlM$0%Ipw0%yo(GB{Q3z^!j6M&u-61
zZm8Ga7ypEj>dPlkmLEghBh?#huQK|S(o23lmOj*Ny1c)B$&%{DoXtb(%MF&SPIxy%
zD1f!bt;%?#nzq2aJI89uVx+Wdofo@%l;k`=mC4)Ha!!g>;+=<hk%iV_{uOnF)7X4?
zLKU*x#XR=X%`diQt3Jn{y0m_nNmR{iUu7q+&VB<2>aH%D+^r%VY4k0t*(EF)T5f*W
z-r;JFjGAV0ROcRajX0e2*r89cAT+;~wy212*Y*YxeJ}B#EQT+JVw?U7eBYPK6=hB*
zlggSCBQuXy@RVHN*$wk&sj_{Vd2)`7xAlwsZD}Vj_uZa;HwLkLbUE12>}X_&nx6I2
z(1f$o*`7h-(LvkOkLu2DWdN1>-r7M6T@hL?T*<aN!QaRuucN|++VJ2Kj>Rm!+h_1+
zUr=I<sn6c3r4@bkgq^iLosG&VnH^#IS<R2lE#Aj#H+_1TzmUcJk?lbSACK)0-A03Z
zmHHG>?Yd2=X13kYUNF%PpRcxjPqT4(%>G?H8i;9;fPqnoqx(V5jM^N}Y{yS7!*=Fk
z%uV8hjnCfstAEL95@fs=#@F}sw4C6n<)K=xslw09M0bCEcE8p!H}KvdHP_2m8sC<r
z_pWvCy!HP4qYV)}94tOArw6CWXMMVBs%MaJ#L)j*L)(py7P~sX9pk1wE3r|!*@2m#
zj&_Go$zS|h+<VGvgd0NRR}0Q9pC%G;ba(Q`{)<gTyXNor@mz)E9}gxb1)n)Y*PKw)
zO)IvgGdwAB=9%t6e{K)`G>ZkU1{@6bL3TpdT1!goRCNQa99P-Iu|`<f9#|R{-HCj7
z?)WS<HCq|c+qmh7LVd7}&(qZ-R8MpU_gnRQu0(B|t|O0cbv$U)`Ff?->B+T4^1^>T
zX6bw<z#fp2a)^z#zef?+H=^kuc_7}Wa!ZSQ)?(=mMQN`B9-s9sVAH#LG($<t{OI0#
zXS-Q1E$__GVdQ^xS@7~9E6HhskG!e7Qg3x6TpvA;yl%ewps<_4rpbh@rK;<Fqo?ut
zXD^#YXg`F7ZOE-=UFCW2#KoHiIiAam8CqPu8G?eg8Z-EE2^^nunC&Q+vGraiNilI9
z!S)Y~CJ8g&R?i9is)P>My!51Q_NctB8`u@sO(VAcCVlnYBO`r@g2&9flo*6opA&9K
z=4Xg)ki4-p$1^rK9gQqmvSG;^*`&vGayz2uGUd`1@bJzH=@u_GYSlkydntZew&XgG
z7dO3&E~{%%-7ytkCmi^gI8CLM`Ix%p(F05C)K-b=h$lC6S9he!ym+ZQ&1gepSYB7Q
z@*>^N2Wo@6jl}gkRfwf}t!LaBX{B`TF{N5KpUt8|CJdX6X0{E>C|JnKKCRUV+|2OB
zYUoD8(oJ99Iyf!bLSMRC(C0%CXOnNifB^D;V9DLIsJHeY$0Ngc#`n*NZm~|KyI!h$
zPo=v<#hmlNi@mc~?OrBxQuF@p7p?grEH!S^^scU;Lc#Y1JC7T*U7jBoHcB1y?ZfTn
zmdDNUTiXBHC7IMo&F<D!Dn7k$E%Vd&Gac#e9xtZ9**Qxnp_k@#lwp8LUz;QCu_L8?
z>%R;Z(_EKdnG-$F{7P|lSHG>*%olOq{VD!}=QHXaMMn5AG3~d@J1#zhfx9{=`$BZc
zjcwm7+`jBrE!=oqAB9Qu-4kcvR%_|n($Gy`Yj;<*yN{llPWey(f|fdw-gTI1|2?I(
zSy{ag*1bzPxig#Xa?%2Yvd~5rPTfE@U25jbJRAdN>9>xwehS#ZUv})i)Qg<v{Ge|F
zUf+^bPm7-1OV|9$gVQ5Q_?aU5HsUDb^Kd)!)4x7QyB=!v3*LO%s%o=#mR|j#y^Bph
ztv=((plbSHLqgn!U3zN;cxKPP@%Xj>5lf~VzW|f2L60@<bDG=bsr5z~TTJ9N-@ldc
zdMs&e<eu~TGvnH9#O~cy%1OU9)#n8@i;Pco3dfm?Tm3g#?vD7f@yf}icGk{?5&G?d
z`udG=b93nEK!||~kFNf{sD5Yq*)m<ub!*xeHNTOc{rV(dI%`XU%Iimu=zG6iy7QNV
zs>ROrDWL!QKFsv#;zTu!o6fLycy=>HaB%S7YiBypBsKrY-P~&7x_x~0juJPP>NhF{
zXzse4<a;7d_ntz=)n56e#}-_)2JEkAnDQQNH#Cy*nKidV!hfp^ad!BvgR0r|wXuk_
zwa9Lkq~`CkjaydpGH!=T*eCy&OwZ1I&Q4@Ds;%(7>Y6qEWgJcM%@tW}x(^imEqq{k
zVv~DRc$~GyKqzb=kTpDamhwI?HQKXfN17xyyWX91^Zv`@XO`bsOKm)masDw&Xf7K1
zc*`xKwa$9+`FplDE%!aS&X0&>4?Q{TbYdajlHHdpY&sYROBT%M5?C>9)96BO)VDb@
z<2{#Yh+wATqXm96U8i@|vUqh}-lbu=&?}@wfVTObf8?X>J~mVg&vG3&{5@=vn){5a
zBfd=MW}O=vFJvusa{s+|d~4nnbFZ=iV=DM$o^gq|dX1V7N<Q*%9`yoVZd>(b?HRA~
zBZ)cJyz?_2sm@r9<d+P;rK0{Q=o2yG7|7b;(9bR(<k4(*c+nvSDv?Z3xTod^L>DcF
zuC!ZOamRJ@DxTC?YdW5<zPB8)+Hyp;SWB+w*}HUrN<l3!`&8$vXXU#KEoWZJ$e?TP
zqUY>+X8!S@RpzJJ)E%DtXz9;?ov~IjYPM#*$mx7HliRY#tiG(6)qX|4(V&}6N`#j;
zHBPww-St(oURap9E!iqTb@D)4^d+m}MO@VZGaHsKOgZ%8u>0o%wuRa#d8lu1{Lv?A
zwCQ!uUq2u77xS`W;JDs%sa0gQQPP74Gg$N_DmYxj_4J;HFN!{P=|V6cE%PVZL78wW
zF0L+v&4IPly1L;D<U(tU-(6_1X%mw4C{1k;44gI>#SESI8mdN1?rb^zL4kAo;zd+1
z?O!V`D-$tlnw|f&sxxly<C?h-Y?9-yQ*%}87_a0uKCxWg*l;#$O@|P(%hxqcvsW@G
zG5WBE4mzoHe04N`b3fbt^8<z%0;p}tK=QP{l@A8Y%nWNj4e#i)t}ap$S=s$5e^pRo
zlcLJkxVY+$8oivmJS$fy?6F{Wrgx0<dH7DoyN+H|xt2?0^D<X4*SC#LM|QjO?P!dB
zknx1J*_WQvBf)P(VD0j`U+er_&#$8I`aJAt(e4*kF1jOVdw*rcp>Azmy>913qa&K~
zfm|0~9UPp?F`Fl#x!Wm=_gUK&Q(2Et;R|^K*IG1Oh^xb2UM}3@R+2&6d~P&NB2VPX
z0;@BwbccjGT+VaT`cYY_30un2Sz2b!?I?LjntBx#^%Cc9&F0U3b)cV3Hzh!rVW^{v
zJ+yUQN*U*Diz12jU*0CVM`&(YD31m@2i+tu=d01MUsZc4Hf`{+9cxO;yV}AkI=$8&
z9Ul4Iic9IwoA+P2w8EzTxJt3yS^+<^=?_MZKgwm6*0~pHuHPK@;*C9Z#~rU%k8a((
zX!$trR{Gp_eH&`$uIDG3kZsEP(Q7?HHy_^GVN1`6_}N^|_%fYpV$juh{U<8&?5`3!
z;#T^nsAokCp4viVaj(YY@xCFE<@2BFMp`({u-_ahZp|)jle=3d&~N1akcXs$r-Z!P
zkw8{tHdErWDEgAHel&1vgP3DFCv(;Y&1=`?FBw!fjEbm;c)m;j;-vlhy#Bht;*Ga@
z7HwFX{bJzVI#;jtekb#EWUY(m4|Z&3N!wO!@GgtF-(@T3x2?q?fd!$pYE-aod2N?j
zMo?d+N^5z~`o8hG&wF!Dq*A=JWZ}Bd?oRuzjfN&kY3DC~aOPMvz59X!JM1j@>1Nle
zuD<<Fb(N~eO2>C6tlQ|A9lu1|<jqFb@l1=Qm)v5Sb?~({&!voenx3OnpTegnFg+Pv
zjBKu}eXbEZ)RnTF)q3W<<k$2*l?gumI#~~C9@%$k6_)Labnec07-Z+lf1<wuH9wVS
zyxv$Fmb+Wev8yxSh3Swf1ARx0NhXb~#`fsB^&>9?uWf%L#F?X+J#+VHTh{Jkr|Zt@
zI!wlIhen&|yK*aEOWJqyPn))hNyd%pbmh15Te~c>wYLg;9^2G+hHAdo%jMS1Uwhf5
zb&}3UeWBud=I|sigr&nSbN(@=HL^#wBb$3?=3HNWbnaYO{jRBJ+`c5t?EZj8w=;Ne
zHPalK3!YE54ic)4A79tf7UU%%cDGmM8)%01XMWhqvE=o+{5ibG85-F_8S7o`&XyF#
z?d?@L>~H9m{4Fs_xV?LWq2Z0o-S2ybOG={8wC_0`_2#p4mlo%|MXRDEqT{+=ETBDf
zxZf+z?B?8$#{0aK7a9LGv&-1=W1@H7w(C8-=>5j4l@24v0@wmSSTohw^*&^2d3W>r
z*T=~bT)g~s4kzQ{T&l`^;?MVftJ8SJ;&El}1)9sVJKQT4*=*moR$6;7EpEfHWk=a=
z-g-fIeXf3qQRE`)2ve_RMXTH^M!V`Bhi;efXuQL_BVNmAP1@IL=~t?B0jz#?HXMiV
z=01L1J^P}mgb+H>Y2-1Qw!K=t*rJxOCAbHi?zt%S3;ORSZcOjvxVf&=%E;sL{ruAh
z#ML!JtM@pGQ%SS?@pgN%_ss0+;eHmmZOQNfk5lqy^y}s$`X%3rgV0Fo`q5f{iC4Wn
zf_<4g(SqowR}&e;s<gi;`EO{t9{;*+nA-JgNNVWCSNr<AS{?@WG_JR)lRm<{yWozw
zc3bSS(JF6e4{x9It$R5?mBt)uoX2}Cav>8^(v<tcWK}gBI3kq1X+3S6g_Hl+;aDfz
z1C<vATl%gx56pd<x@h>wrmrWTHtq`g9N?O@-|d*(TW2#9i4ax){pUxiIIXvI+T36n
zjm(<q`68uq;i~jDYU7cN>!WN8?{DTby{ny7)3D?bQd`v;eSzQhRds$#rOAV&ma9Q^
z9EF9vui9L9=h-E8o>R7a(ePZ!C?Q;?y+cx(ecHA*_nUVY+?dA`AVLh{0}h*_@Jl;H
zse}ifNDk~&lFU-&?3rn1o~NAadvpQaF{bG?Vz~+j-ZhK*C0X9Qz30&f9Y(VT-%Bg)
z?9~hMo?pJXtnhS-pMHtZ{P#5`w1Qc;tk19a*x;QXN~7y@d^!?J99_CLqro+z^`SL8
z-R0LWwz`__p|RG<KEk$Y)ozw2K7}8Z*1lq>U0HkrRXTKO`4?TS@tN`NniivDRgmqW
z%!hdwdS~Ku$ZbI-4EuNx%a*>=+^ipWJs4ONIl#@<Uc7O&^@&-^7v<YanTsUojk3da
zy5*l;GFx-!uJ2378O&R4O|~!=wEJZSnAmpT<fc8O&#kh_fcl&AVSlTLD*d%QTfb~x
z`(fna)+NbeYtb1nC{}JPZqn&pw#sk+LtmY}-K8-ompAWzoo!_)hik-NQ97D&0y*=X
zdI(#Oj+#&Re1r_?z6@O$jZ7<1ty0u9L}87+q8ogpBKO_0*|Oe>{Xx&E`wY5q0^LLV
z6F<kUO(o*_HQ*3{<KWd-LGLf5|Mi!z&c<6W{ZWG6cCOglax85_3i0pn2~&v$wsPKk
z?}uv?X6*Ntc6gPSdp4-_iS^=R=XdhFxVv*&L=)`N+S_I*-rw%}r9H59_=uUCXn1S5
zDRuT~4%e#Ccyzs7%)>%C{+hi|hofV2-c!!S-a`)6_ut1&*VKXy`Ov+0Y_1APUOgcu
zThBkfzjBsqhxlvg6vb}2uY00~-WDoHnOg>bd(-a_J44BTW8aF59Zb|*sk2MxU44~0
zW1z~cMErz}l9t-T_nFf*A5srhtT(CI5d3Y+XvF#33e#@JL{-N^AKR3EC}Z9o&{k<R
z?RtH!`WiWhLdDH4&KHN%*Ot&+XAIxdWoIWQd8|n=p)kfoufyK7+3L;L<{|$`Lc!wH
zyluB(r61hlHD9XpSeiV8ZFi~3RmCQb&g0=+T`!W3yxz~^*}bRo(8iHo%Oc%`x}NAO
z3PYT`-L`tPUsck1Yo1>}OH^*L*Ihluwx{wtb?kJuF=Tm~(CmEGE3VwKq<{};26`za
zt-r%5Z{w0w<)Uk##+{<oys6?baa{D>;#Van@`h~G>U|R}dQ5t<6lX;ekMsm>_JmsQ
zGo??RlkP^GHZXW!62&+3Y+23gp>&~d%Y0nIw~X9dd`&|uySsS~%-f}OUwUtd5uJ9I
zsc2~qcW!6w`qJOi*C)t+x#!#APhx?!!tNj1KwZt{UAbbYdV@CWK%MY|MZ&|}Z~BJ+
zn)a>daoRfPk;L@UkJg&HTW@!+Tcy8_ic5jffWzN5-FDfP?iVF<6&G}fU8!j0OZ#&4
zmZr8Gs#xrG-I9upV{q|XmM4K`XN}kPd@El$UC<``iBpo{;PsXdgLkA<)UH|-GP1=!
zy?t@UNZ-gp2C4@mLPMJm_wuc};QHa|rf*F)Ms1RwHuDlwd=loJ^j?0xP+RV*rgfEj
zgjs2Q;u&Ax_0M`FWY~uu2@36gBzQs1hd1Nwpx2u9Uk;ozpY!6Ctvf^8V<ORL#n$;x
z2A^eYDLIf)pMiv~o$qo;690Om{bOa;{=K4yn>IO2_jY<Q8y3a~G}bk)^O8P*4PdvL
z#aQ|BLu%>m_pTK!6<)(C=z8bY{lHWf$vahyCs)Mf4G^?_`?r5?cH{Js@tPsk^7^Rl
z4gV)z4KKazB<K`(DyM#WT<q{A^Vwg?Z69aIALD^jzM8e1t@46QdfLT$^hQq(G-mGW
za<Z%0%t@DhxXC-v%<iLcS`h>HGs~1Myj^)nY^ZsD$BJc5>8g?&Z_)?e+W+M;(;C-x
zFP?w1>xwnXacK9|`6Rt}V;_gv@S%lzfzsMBrS;i^X<;)Q(^sWG6zFjX8InwXo0LT@
zCI4pIbamUBj_#+nwkLPZzvv<=#1%LD$<qYS+V#5yvzH9qXOB6)`qsMHx5UqV<g0z@
z)DKME&MxN&%IY5GSB*KEMw4eYON3)C3gDJ3Ru$jWNmpIiS22gqb}u($WYT{7RmC>-
zuf!kI>vtwvzxBS=$>=GR+A6>HNbqMgmx?9Q>1=Dy$iTKX&XJBs+dn;6LnS=is1~WA
z#B^rCENYGgy>tu$?D3k~XFh5_;1@c+acx-KT~W_btCNj!)px^R-1t~&K78nz^-NQ{
zi_MI-b%yNg7`O`jz9nm9btND!#=-sv!|vkL4AVSo?Gg>b&R%u6BjdR)DsHvXcG7wV
z##7bWT0_BkcaGM6rJK$zwzgm<yNnN~%L=~|QR=R<Y9;8K|N1W%bmQNfsxcaSNm#tE
z_x9iSWf;`WUmvv`x+*K`M=$Za#!)F)tA))QHI(MHOpiyBuW6S>F!<bj7VCoe85}Oz
zG0O)HwD84m&`{U9?fTH>Y{KW(oo}QcZ~5!vHCFYCr}BS&e7>T;M(m1U&vjGQh_k*X
zqn9`{hNdsivzJAzelz#qlK*<P@9ngadp#pp?5VA{sD0fKwf8aXLRI2tEPU5~+f~0)
z|FCtc9M39K<vH@nNNjVv#ahEghgVM|*#&pmEzlIgyCbThH3vUNmv+88hCEW{FP*zT
znz>{1L!{sMyt$CkaNgbuaigoo4|Y+V7lBjX`?2OGtJoNZBwoF{A*a7bEcWXMC4nMc
z6z1`$ms-8V_;r8Zt6r8jAFrL_YbguM-^zC_!{UKXicnVO)%554p_=^G*7Ag1%2HZZ
z(y;+1Yu0p_Xujc%-MPCIsn57VM0c()nI=xaaqQdGqlxmDeRZ5_L+84<)_+clTfHsr
zhy{0W=#y*B(cf6I_BJo3?;IRrSRr+o+Gn;Y>&N}(B{UCCd0z~xuj{|=R#9>2_G|k%
zpUk<vE~|DI@TFQxD_q-o=gdo|j6zixJE0w#!?w%TYMl$u>h*Xl&+baldz`JUznmmc
z<9er5{MAB@%RR5lVjA}8cTO|BHb=eUW81Bd*R-s%=gFW{i_)^#X}8bqTh6@SHJycR
zrl!npi#NI!j^Q&M>6$;d_j|jE&e))%I7GFGrT%#OF#NgsT#!m`W#@G`i>>V2Orxk>
z+PFx}vAF7{Z;6m3Ri|5@@9KMvq4_*i&Fl)CS*Jq<H4e1V_0?-p-4J<KxIKQy%{@0h
z&e-=rhcVTR>TSnubzQS<7c6a$u#T)aR_3|Bxr(%-ez(H5PN-pb?<s>$C4Y-6uEZ{u
z`hr_pCu`1j-){f-dEYZ#Y8hj8)9@0(K=+4XN=<jIFIUl_!giNAs<Igo>t~K^X;@cQ
zpSXV0ZFAw|)}88Rfz|hoVM16k@ax+tU)C|=<vuMzl^+%$tKS~GaDDK?lRG<X;u#-8
z^V`R-KUK|3duCt3iDspxH1n^s@vTpeyL-z*zytbFah+;!vqPD@*F&8ub7Q&z=V?wF
z2dI<TYl)fS#x$Sl(5sXO6^%<qukw|~FzD^5vKc<Ov9CZrzMPirR1FNDCV8)2Aw}og
zYb~wk2UYoOOHwg7@cErHf5kH7-utya&8H(>wYR68$tT=(-pJ<%-rZ{&THl86d$1uX
zu4x)rU0qdQEO?LKZe?1^?4Rn~R20V8vA4kf;4WuIH`fpF|Nj}Ey58&5(tF#DzjVoE
zznAx9ZZ+nVTvWey_sxMBTI|~*kF>(=!yNyEVr%~jeDdDseNo`*k?y1fgW%h~@-H3B
z%559v1G(XJm%mUUM~-&%@Z~q2j}F#suFe;b+qWFCU+T=iO)p_0$*ygqexR+q@8hBH
z1>T$@o^#uZs~;W7;x!2yq_;EX3JCFK6|1V)QB$k+;xp$qyLZA3Rqk=z>K38ly7J4B
z`vAQ^>Z<aed8kFg>BU`Ije`fy#-a4|YO7oJ{oXr;w=CEpudUwYQYZMAQA^&&<c9Z>
zlJwq|UNeJeM!7i_OgA&-3OIZAAcO1GN9RlYKUq!tP@%hSVamyp1&LcYIg%c-R?NC=
zdMMp?-(tp^Cr%C*80@{ub}dWnLG=f+^^%r5T==9P>aoeccKd;IWfto~&3Ip^M~@mm
za1iu|)6R&niYKpZ+3Baru)qDdi>YOg&m#|-T{)h&3oWa^?BUI+u3<QvUic{JOY9lM
zvm)jVlMin%VW1v$yOi3GftAJ0$!$aTnIf7JzN|f>9_2}&-$`$XU=Ff#JTU{R6uZG`
z@7m#zV5lhes(F8me!;G+M{o4_<|}MSjpI^K5(;cv&%phP;SC*<Dk@hxYC#nh9n-Vm
zYRHnNyL73ybml6D)+sO4;#|WdcU<qXhk;<*u-`*w&bF48^#LPyRNU%>8TGV2v4|Tf
zJMBMtVtC|~?Unas8(GytUa`zyaj)c?ITr)>-CfIWmLcO{qe~TZn*%p|<!jG;a>*wv
zYfqU%`F^+gus`5zT0EK(oAl9nP2JIosJhmHJ7x2}wZ8apECVsTS!J!1Z$V|OCo$*7
zu?H2l7w8Yd4+v$jTHJp2;jw2U&2=ha<`y5Ce%HF-WpXe=EbQMsu5`;SSlvjkP2*>n
zUEokpZN=ms=Ed}=yO&TYp4+~P2{Cfq$#Ghkz<aTxrso2`1oNrIt}j*=aqiIxze0>w
z(iAVeJ%3N7{N0GTtZJ|6(Dl+jj~UykRz9;m?cQ%<h*(96RQHS8qPF&<d%it>;A>fF
zzs<R8V?g5b#r;j$&rexYs@=WzT920s1xhD9mO|=bdi&1KmdjNW4KUSB>X>(f$wyWR
zU4HTZ)b&*XaWqZWgS#b*dw?XkyDbC=!6CT21b24}F2M=z?(R;4>k=FiEVygno9Dgz
zFaDd|o1LBMp6crAI_Fg31fXe}dU)5gJ2rKiJRVg9d`GA)+8MKOkFCz4hib=D|M=3!
z32!olAv0XrX!HY(?;OESX-kR9jW0IPn|FFoKm0ZEk5!5Q>)t?(a;Js`jQU8?`#oo(
zLoDM))~_?`fOxmIP3=!3x1k60-lo63&gl2&oUg3#$f*^@KEolPbR>l8qo*JS{NyxQ
z?tWM$lU`38!k!JnOku|bU`yCS1wW5fYsw)T!X444#-6ECJ*guO?f?pM5NK3jIEafZ
z;2XxskxWH1s!893)LQ;QV+i!N&53_kEJLWhJH8-E!V0(0NsAU<15%eDXNNQTislCc
zAFs7nWrnA1x9-rwvf#I$(ye@ieM4=db|fPl`=@7Hy-?iW|EMu<5!n_Ap|$YVMs9Rv
z7%Em0Dfvp$_3&ECdF7Bu8StZAOiLUHGwQYR?%)ptH`q<S?N)%1H+Hvv(Ltqo4f3s}
zK81Pp1_L-Fw%vKV=mRVw0%A3j@Mm6_4KnqSPC9^gan~i+&&bI8Yua7dx4X$--<zPQ
zXR?%hf!+#OOK(0{KhJe4f;5ny=(PMJSH&GtaFWV-Hn1fBpipW=vax_Y;#NMlTMrxN
z;W`#MgTi#5E^QBIhw1>BZ7W=**I0lmUHghi7lm+YRN@z1isbf>)o>`;1zdI=Mem0U
zQ>WaR=f=+A>uI$l-_RLzW=OqZ7RSYx9XB`;E>99q!Y|}_^(KOer+|3y^BW0eIPN()
z1imw-8BIh}7<?`+?I(@iLv{$TzWyk38vaSM9zdnmTh3{LRn@#<{Au_@A2<Hz$MPF6
zfY)bbwr>&-qb54W`c=vpp1y<lAJ8bJ9iR8UJVyJ-U9|`j^!@{`8Cyv2P{Ud4Qvdt8
znCcy7UbXks97qIEY@v}LGvc+!vyNm1pzOj{69Pb{*FIOeN=(}t|F|&<BZ*EjksQ-F
zR@&h<eDJ|^Md4y6p7@YRZ=SOKwHT2!`t)f`iC$7YDFNS<4Koln)c1S$=K~hWk49cS
zsv22h1WG{6jwD`=Fbq)2b%9IcoXh8igIW^KOz%6LxBH<dq7N_s>HjOuuidFSG?Bj}
z3mORBvS`>uYa4!mHtSp&gCqkZb#n6Nu9nBzYPH(5kajoUg)b2F{!kVVo_7lBEgr1g
zs*o1aL=S|rT$Ije_vEMXBTT1h`}MH-iVQynHIM1DkUm$iFYY3J5>r>{t;6)8ps>?u
z+E!9s_VxZvSAk5224~%Cku?1mq#Oz;CMFydm9lA)tnO*AJCbCrU@1o+v9;k=nZINF
z+nkV+ijQ=j1poHK=IvSAN@<q9nv2p{D$}Q18Ai$PmC-Y<-m2IAw3R0a4pu1N%&rS6
zR9tlcFyInq7_SM%4Vuw^B!IF2mL{BCc&f>75(pB0%J7SAR-b7<kg)rTF?%jRbj;Xq
zd~xU8k+iSidW&ph+0%|Hl7g1%dbPoKWlel^G4Q+IoxY-6MUGW6>%>9cee)PKsF7;m
zbZd)8kbs-xSQgf*QIZGb0waq`04}s;-<}IaS#6<nVPU`6_ZxdB!E1z@rk5vel?J#X
zp0(;Q1Vv?4kh|8(gwT`9+g)Cbt-a2qDkDZ8=w=&ZcJ+@cx46YuyD7>gF_ir~d{QYG
zw89!w>O>wdU&h++{jX80lVSSms)LDbn#q1~W5!2=B%EC`7#W4Cr@9u_{769Viq{~~
z(!=tK9Etvzpn}O-9OtpIaqfiMuTK^5xp2hYngfZ2Zy3zBcD3%hSUwo2R}t&4RF*@U
zYf4>owoZyjtD4ISWe5Ny)%-OPxvSto)%_R7kCL)unF!Q~Cja18cI43l-Wq$+peDjA
zPxk)a-n?yvrU|h5CcMSKQJx_)NGFmMH}YCHUqGSyGWK3jg(2?M;+9#e^#Yg<iuso%
z;-Pobxse2~L4aipaNXf)>u{+=MpKF5n%#AFZr$8?O_!E`5tE{c>tQ0jXh;24yZjk0
zqYH?v6fGHNyXl;~FPNLXNQQajZahEvb0;oiNd5Ywy>4TktU^2Yh9f9C7lyRHdt+r`
ziD=pse;;#IE`GhzKwo-r*jI@TWSJ&lHHWV>0NQe=E-|n1ZGd(mN=|tfPD@WlkdG$G
zl^z;pv2}TIbJ|>LN{U&8WWl=h#)2I3s}!%;4W7!P*fN8c4o(}p^9zhmy$`Lo?3GSH
zrq-AaJ-swK;wO+^LA~jkF1WEh?O<sFa7Z>UGM4+H_bN3Bd<JBwEPegz^WO!SM1~{S
zTiySKNz=zqMiL#?{K144BGNrZ9gHRajO38`^y3~ySJz1BnE@-bFn#3`WHt8ghnP1p
z0Wt;4F$?LH&uRHl8)I`$n+wf6TIvbXQTvBiTHXJ6DZgRV34OUC99W@GKcz(VxC`od
zD2EecfB+P!=N(*;;KmV?VLS5Is#m`1-5bU)NI>YykT_D+5#}&=-`V71+N6xM1)UN)
z83p=E^}9q|O#7lL5)$ZGW)(Eymgf~xr#kPe^Z^CkAs?b_yF!ST@+@72A<uZTnLNN|
z`hhf&p5`co;U`Usg_v49Ir9>{wwygiWa6B{cI+?BJRog8?dAH^k36kg&ghdm-Y3(|
zZO{OPJQn$*sBCec2Fx7(Dyr<SbNEmYh1z7;j%DBkh-=ivFFca!3L$8zZE-Y^ErD=D
zNHi5qGJ$K|1vXB&Elf)U8QK({%TiY;2U)ZLiB$Q8d~Z=DKGCB-*C7;ip#@P9!?x7Q
zP2d9J<&Rk>-mBt$kUlT^24>n3>yt7M$B>QMQQ@$$)oYPF)~Vs9_lvvA`ggUL@DSTZ
zB_y#)lLKYww>`JIEcGv?uFWtJkpR~OK_c#z;-RWumk-wSB^<+y*ml{Wt-teZpv<Ry
zTHFGhIhSX9yAMNb(+3|%?O!N>mCpgb<vUe|76<Vr#2%|;LM+w$uk=MA+<Q7oTqpr<
zo45N~+*#~*SV|ISMRBSYjBEvop5<C7a)k}KkoJm5VIdtZp;lMjOl^IguEygnUi8WI
zoNGg*%NH=~Fk4&}P|p*V+l}4H(3~4wSqg^z;4;t^!XO+8+H*?HIU?K4OA=R6Ma>V2
zrXmgz+K@dX4FMF3v8t`#nXaQ}5ex*GsLK{GRg9u1vPiXcY}+O=7jMYt^UKN#t}!(8
z_?yz&bljJPY91^-KK2IgK`~NlIfa*y;85wpc;Dg|j9Gqicdu|0a}+QM<&o9>D{q0t
zs91RYh}}t1z3-|roSWf)vnj{CP&FfS9aL(_m@Ot1&#e_jSUC#EN69R2f<ID`ugs#;
z`tx)K)6?ZKC&PcXMo7<&(7JUsO?sah5SPA2?A4jdQc!{J8Oar1!BcH+Q76aHoQ`8S
zJ~ye~ha2a6X{qDMo8rmt?1s+6>xDBo0xVLtm|{Da_G7iRgI7(Ga07?ZwNe_rcgR$<
zm?c13lX%IFa~xtCoRWj2<|-5jd14u@FW2hnY$o?8t}ll~L^pQz{S?siCHHGYl0ylZ
zF`AvH&i!}LTLHP(_kMT4;g!R{y(?I%MgRcCaH|<PxDiFJ>790N-Y^Gi7JD9RsrZD+
zuadiAyGjxFtTGI{>aUaCOSe_j8A>VV2rWD;4$w(a159PuFd^loY0WAvu5i#%&#F{Q
zPUEM7b8o&;bY`!lI<jh{Vie$$O8n?op3_yv!31=~-YX}$h8hs-<g{r+mdlBAQ0gh{
zumbn_uYF2xvodRode`L-VLbFH<KjGg$@3Hrq$gh(!v5{{GG=q%`b#R~DVpQSo0A*)
z<<{c|{J1ZzIxqJ~${Y>U@L@3!ZtsVSW3gqldk?VuM_Dh9gGb;2qgmW6AQ$qL2p58V
zHY!teOMCPYCEIbas|OPL225q+PwgxCszrYIaNInmjRyahXyDWt1QE`>JzBvY(#^&X
zT_g~X0lBRES|(sO`{55JpjX?<jPR{c_)lPdDU#^LzsenqhC_^U21(=ab7oB`bJAlp
zZ#?X!4LHkIL`~E83?w7<#MQ#g2AJg36Hoq`(Gy>@H&Bc0wW~IU?a0N&&Qo|?h4WLT
zsdF$`vc)(U^<Had6VOYmQ6(qT;!hEg<+#NT(+u1+`JWKv_HzPF+{e~YQnMFD(+j&3
zH1BTdXk@~4&gh5uE3Q{osz;__O`jMKLt}f%Gdh+#l}z!Chj_=i-p-{mv<}>5o?o5t
z`+nSKRGG>%dOUn_u0TUgbec%JdDtZlKe+sKfbH$-Qe{hr6;;M^<60sA>lF~P?P;q`
zoBi|HLTVlOoXeQhQOcEoC#J?2yuw%P>&R@cJIy}aGViOMfaKswz07fZ7z(d)^Sr4U
zbd$Rd*II8!3yh9Geaq3TUv6ZE%f-F$Ex5X#NU~$<7-+*TWsu~cNiJ)<Z}bsS<t9uk
zcr)BGdUc<Uqfra*@aqU~^X=?KjLkA(qOqKYMrO$-OK%ogg^pwJ-~C%7gl5Fv)z5Kq
z2%3w<H8y|gK6_7hc?VmmCs9M>kVrm`5t_HCD<!H8euL*RS@FK9FyDtvtuE|Jh6gsY
zMJ0X=(9vEO6IZr;lbTymvQ{!i_oKWH0k<>@Sh71qS#C_eE)}7q-alOJkflwS6i+;|
zb>>D2C-*2q$H)(o7K*ZbV{OY{0}sB`WJ`aUz%t1gBrv0mjfaj<%&Sq@BkNza;>Bgw
z`Z=leFODHXGg1J0xwV5YBb?Z*ciZFgEg5kVd|i@ZtK%q7xy|z42W3k;Kgux>S;3^a
zNV1t1E1vpD#|~&Tt4J3P78^a7J8q^IjlEAD86svmV5uh2g(1Jn^2{{#L;^~o0H~O-
z{-%n~R85!rZwSYdmbeJYmEP3Dp(Kf-Unc`H)|sgP^I8Io$s0BHnOhTc$p+h_(&1fo
z5@pvfDOUEeqe8}v|Ap#q#0Gx_S~|^CSKJ*Q8ku9h=m5IHmp@=)d6xn#mHTK{t(t3r
zVdSz@dW-CqPJ@x=P4^%2+Mw9yNnc|$NzRjM30+}uV6IfHQk5<|0NownK_1|Zv9b@|
znz+jC87q;MHJ2E~GzNe_(QDPJxXs~8xIVI(0zG8hE<sps_gL1S%;+RQ0<fBNlhdXZ
zBOV=6MAFv@P2{>lK#XZ?-gz|8qNosWIoQsn>6bg8pB=YE%8D`#(e4f)+iUb;XTuaT
z2+uYkz?|?8sGn{5xuDfpB{LjCB%Lj%J{Z;~vXijB4O9Ig!*VAH^0O1?{|cU6<i3Xx
zi%EeKM3W-LL{G)|b4dP)#M+aSE-rS-;047E8*TwvzHG;q?~LJ~&rrkAeTRwivO>Aw
zh_Kr4`quoYS#3sgA3aa;HF~<`-?ny$n0u>I5R$JD2&D|FWH3j2(iFv%{@wi*BsgPL
z7i2Ex+%Kh~Dh-m}RmLZFs(6eMlAjD?RjGhCXmFY5mB$ywkLt5^OH{d%FKilv)X_Hw
z$wjmknmo8bA^as}=`?(r_um9mRKHUBoc4A&nd?_Jg}kORawih1YhTT3@&2=*1cE}c
zfpUhQSRLgm(id)x3zg9xG7_{PAixwO{U*v(J46Ye*?ua};_zdVMr$H|!1w!8p^W~<
znnZ+}R}M@wU=DlpuM?!cj~N;=9f=5)1h)uWnQqMKj*Gd(GNwhEPgj<^>;+wLot{Nk
zMReNCU`9Yoc|lK30Tl|pIbkSjD}zd|*j0RM>sQ&q&@hHe5`?pi=uAneA^IRJMl>{)
zo(<c$kqnGENIKJ{J67{)pjJzI2kfU%nPa-8+zaWRxP0rTNg?-0+I>?|{`W@oOTs<r
zV4#MUQKUm}lds_*1w;A^Z)gfDRSA1zXLp@&$*87*^sOeY8B^6Ec>v-SqsNJh<cKUI
z0ie-yUuH&R^P!WY#g5PGbuje^nMcY@0JhNK1c7;}#3Mql8Ac1xMCPF|n<g6159%Yl
zR<8Sf+}PWNuNTzCvP+XNKc4Wsg$RhxYQaSvK4=DlBWUFd%D`;eY0<V|wog2`RE_|o
z&FGj?+mK3IQ)*iGCi4T5kmoM~sOR>6_ZqI8REgcEW$=FIsnNrd^C?kDB*O!eY7LNc
zSLmD_&E->5%>tAWI7isI(m^Db09(xo>#p;h87gnI%$tj#xIx4`b|naMUIz^|Qz>FB
z>e|aR3K8$>lLdjO=0gcli=Jme#@s7n+lPfAc~eZ5Iw8LOT%Z-X(T86372?%J|Dyib
zg!%G{;hk6djXB%&PWVVs58U0w_qsV<vwgxWDi|K9+e?anDWg$QbM#a=?ZUiY16fk(
z<*-)vCDXfMFj{?>v$^cRa*s|Q+9AitRc`Wu<DaMjPu=liY=n||X<P&VxD<|O_Quq|
zz{jEmR9I~p7~;}w_FqMAkq4%5!$e#*)gonXUJ&jXftb-VO9m8ux_wm7nUy?^uMJ~(
zs6jLMn^&yg|3;dCoH8=8XQ|BZa^@|Hs08^v4deLguvRO3<m5pB*9_me;=Fv?j2MjJ
zG|*yWNd%o(<!VUjFcMMx5yCgW13<3QF00|0!lb?~&$z$pBSQBunMZOyXyJ@e%FcC|
z<$=Dty%mUXtZ`cu{}wlrCm@SFSU4|Fzqdt&+qIgFzd5J^<2m<Pj3M#<3&pH8A8YZr
z`Kab!hfEED5R|+eW{7QSlM~uknxzr!PMNUZqn0R`>dy{#Uxr$#5JtvdbX~DOsLhLV
z$@pJhd@-7`vOD;F=TQbFc!Yt^`yPljW1F_xNDyc+<v_J83TbZind$aiBg4jQJh+hD
zCLU)#`27WUGx=AI@WuCUT>LCRwcqaqPOwDxhwAYfnw0q*0n$?(>dgt!L<i6tF?}o6
zDkj}W4=_9K>Hf77a$H}87790)$)u9CHOHk%8OHnNxE(>ylW^~3M~JscJNjx$P`=<d
zJV2Ec^o&FEcjNoKJ*F{gU&J}K>d&i`ncm`mc4MBHHNFC~CjR$0!0sa1D*)DtFlDy^
zFGBJAU!Sgmj5`M1?5pZ=QiXF13(kIx@5L?X@WAFmFQHK_t%s*MQk^P+_6g}R7WE%i
z|B^L7r%mE!IU_HWb!mIE%(DDXnd|^~>O_BNPQ7eDS6~a-N6+eNv}rx1r1Doi=%b`j
zl?1>ji#t%|;@z@~n%a4LUnD@y?_7En{|oxf`d-B)<_g)6FuxF*#8e4@1V{WUX4bu%
zk4vXKtJsvXQE3^R+7AUf&n`Et6*H;jU}gr@wB7*7J)D?d;Ffsx5QP=q{;wmG`lcdf
z!*;DOg*7xA`w<(OZPJ%~NeNQ<Cc1Yxg}9kK<`VRY_@2*~L(NLVV0#HKH;c|CF|3G{
zxN5HfdtrA0w4j%2QVJRH2S@|dg08h=XI!eExDry_opOA82UdzBaC1L;A2ot_U#`6u
zvq9ywgW~kr=&3qK)Ilg#K?d(C+2y;^S0M2KprUc~hllRXe%D{>y-=6oRj`HQeT%&E
zQ^V%(1Q+t04)^iIn@z@3PWn)txa($cz}HocDZKDw9IfXLEn5v5lgz}o3EStgihlX%
zvON4CqodOAu;nu#QgR9z!myttzPbJ5fuh^_tg1?rd81D5p^!TfGS>(Z@tMjy9&NxH
zz5;!NPH)`|_8k^e%Y75yu0{;YQ*Zqsn!onMvxXWdr=9K3Q85(D?5x8ZNfP5CA)MMC
z4qlT7Q6$;1)SxI6Mh4Mk!hY7ym-cPnhMC~4Y(V7UgoaORFi!{a*xSe&o;wx#`*&8m
zODH^Rwhmims*+Ii)xw<B>xC65vz|AvzfOw0b0_ng5(uE26VX|b*&fxrT@On+76a*a
zOVJp}un1W8jBC|gzT%fioHb`$eRnf<y_(E@ag9tui+3>U<<<)J%^k<F8U3gIwbyqs
zaqA&3chjF%{W@`K^z}h0*(*UT>)w5&iC;ess3JlD_Z$-O-U5Gvz3NJnVZQCDp~<G$
z93A~<Z8aPlRWke~O!MrA!HrnqySo~|L&wkSEzRcj7-Q@4U2gSw)yH<n-B3)^qV3#w
z&C1`mWJOBR5{c2ssUKV5jyM0gllQLiVs7iOPHuNt5VE)7N(xjM*x6lnurE>$S{k6f
zMlf1+sKr1*UU9PiWv=w0xMfkrHBE#PCA@#1r?rhfIpOSK2x)*LDe>j$CnF`Vr+v2X
zJg9OGDgV>(5|QVDmti&1HWtOlHXT^KHrm|Q0@CkW{qhmOakea}n_1E_8=zY!$o*JJ
zsmvALAO5)Ei%98vn{XVZgJg<GT%Z4D-?<M=_-)&c6ObkzR7{=SpSAO^I!rH}-}bjj
zjw0X6zuGMO#hFw2e!WLabpxd~rmcj+GZfyxLcWAy=vF^-P5*_-hXy5l84H1qFX9XH
zoLcy_aM0A0jjCoIJL(U#kU$nuDX(BqZCAzla-w2<95(1ONz$zX^6(X}f_aym*&84O
zM~Pc0251bInF}Q>;UG5*P$JsF1KR0gCZx&ib!R98sB&BXdE}gzLy<~b5T;1-Mmc3~
z_62x<FD0B6i*=ECxH*~@BK$)}my3sVp6!0mi>IaX7rKm5)Gf!4;l>9r3?A1c#Yan`
zv$KSI0+@rzgY*fbR$_0|R+~Qyo|Q-DXF6WFbU9h!>!@|lO)-G#TZ4xiv(DYk#)%)~
z9!(P7)1Ozb{H@Urq7APsCsQY%{g@9b9~c4%W^&pOch2D5DN(~N=hS4tQ{(2eLn;eY
zd~^z=?R=$0oIB>KN2uYW-)#YPo}sXbt>FP-S2}|tNR6)cz{}&jj>l6J>(-+^3w2i0
zX~!2Xlx%VbroIk4iolUxr>=IA5k~t7LdJ*J-xOcg@3m*YIBryN5_xZ!e9Vi7!R-gZ
zuRGWvwvf$P`$!gfJnZlfCK}IpWex?le2is=EwFUjVf)|zxaHf$Yz(?@Ld^C%XXt)@
zVp4Bb!CzP?#TM!i_E?F4i@ptaQJmExez{onG;PRN#xm%9xc=zZzL*4Cg$Kp>I~KhC
z;U_!5YJqQab2`*s5mjtLT_X$^(*@bVl^e}WFlEhe@xt5*gh+U+Z8p1F;!;Fce*?R)
z#JlC&lJj>4e&qf)Q}&xpKU{;mNG7i)*enpbW{n5QD87ua2V;pQ!EboFBI_XMokQz>
z(66Gr(a5frOI(KPfjvR~N4Mel)P=g~b=(lJ5rxxvi73SKFV4%1G5EBzTWRpeDzpbT
zv$%_#jO@#RNsmLxR?}=UG)cA7(Tto`PqXiGFNIxCjEEfsN)fsy^ATsd=Bs&F*L@Ga
zZmoZH)>u57ZEG|3eONkzQQmiWU6tB_O;w~${GSj9W>bOv*pD%BSDRTa(ZNK~OFEo*
zo%~&aVGp6IpT;sJr}z3lZnzRrw(sRmdT~UVO7-8TV|~wDA8bo!{+^{BOtrl6waK8#
zM`Z4__i*=YePv|J@i6A^wIzwg^YJT4u)OUx$3J%^4KURDjU)VAku=M?Pfyc&{fDSp
z)b!}nzrHrJ@hbJ>_$2;$rLoM=se!Fsnl3)qT8xolf$3d$nm_J=Cd5h^CKsww{a#!#
zAlfWT`e~eLguR1o<ifl@f9LVvW-nv$j%>n@ddzj-OE!U(s}Y}upy+o!&Uj^wGm%*7
zTb4KT^XYSr^JuPw06<rOBl1iON3n?ak*ITTk|xY?4nKK&rH0v%CZmkKt(|o@YCfdf
z+B(N~!3#Q(@Cnrcb{$N_aTin0Ff_-i+c*1VzO)owX5&9KRHXe~6(aW<iXo;rvBE!o
zqCu=&)ggEN_8?7}Q*JZjppF_!=(L3>TNph=FO%1x@^9&um4LNx#fv$atnjF4giU=h
zDb6(shbYkLW7I};iO^eeIPnDifC?Dy{!pw^iHuW*x}!_tN(kLvaM1?OeF0@wajR^I
zEn{0WSafci>Gbs*Y3qyFoybekpF0V?Kg}c0oo<dwqr^hm7OlP5BU_52?FaLFIUAlM
z6k*;H0RqejB-A|DM{*;1-VMNdfp5wr>epi0Xsi%d>VZ|EsBAaNUDzwh?Xq-e7Kj3q
zfTYdXt6t;+Qlm=N5O5<VUjN(Bp56}*QH2(qDG!;`jfYf&N||WU3QHpJ6@%>jY#hpm
z788I!&zC^9koqU$0P<F?uN}7VvB-GU+uRJZBxIM(15%s8IhTypn8_<OO=YsbXq(3{
zED{cXA-ZjyDMc{|*6l}AVtgFk`{9Cb*A!*sT!RopoREjP?J1HqCPZ}IH0^%=-=v>s
z7Iymt6|hp)aZQ|Zx{MOEU3z}?8!cqrM|FOT(K@9~_sx3FAJ4%z6}s$))NB1_K1`Z^
z;rbWzQNR4Qbf+K>TzQ%~z6v%ut*2VJ*CxPBRRvQG9j@JFo^UBnEeKhG{+zuhg~@5Z
zMChg=rDd@POv$YLSa{peG7#=<M>g3pX^m5#znxB4ZOExISpV)yu}0*2pK{)<@@K&o
zBG}%iB>R_*=+ns_+y%R3P>PZadkdMQw>PjAaPl4w^v><eotm1m;zhgp^v4#(Jp^u>
z3-AbfaGk&Bans;;r<8NcAukUXOewJZp-6ud9_DB7aS@G@$EBzOY9#RA-{oXsP<Fb%
zLZl2b?}0CZI5R4{xFg~*EM2E~Q-h)Y$&D?w(jYsScDHikfy}`a$4HH81p_+lh(czL
zREm0?YbK4>`C3ulJH+Du2u|?-w?<;G$sQht4<356yl$#)f^W;IJW6iT%ggzxr-`{%
zF~B+5Kg2XN5_b0}E!x3y;uV;KMQmNNFB0}=!EgfDjs23hH}o?Hj>Hr8<sh=(^E;g%
z4nD%Bb1g0ZvyL*0o)y1+;Q_#IR^&4TAA^zo%C&`fSm(A?3)2OqwlRTY_2cGyZIH@G
zF`spCt1Zg2Xw*6X(b*BV+usdNV>c36Gy<=DUGU$;hAmNC4<3m=*h1)Po>d5+panZP
z{@b!eeZHX^ebC8d?v^b-xL7>=NqLNk2P|9g^M<Y_uW|#^C`N5hyY4M16^_Ub7jR2d
zHoYzO9L=gNb%(j&JUw>D3s@JMbly%w;DNm!iFeB)7p8CH`H?oi-qhunwt^{+k9+|<
zJ?reF^&pmJD_tl#U^|oo;tswa*nRMV{!X4r?!$wa=}ET{s)_F~tcH<c$hl_R*v+wG
z$zBZctnXKi=!H#}w<YofHP{V`)nA@m$n{o(HlhQ+SM1nXP3=Ded2iY&A&ts5|2jyf
ztO-GanfX)>=kMgzjt-88uE*ZQ0H`g%%89Cvv@pmR+Wu0g>G~?~&xi%6{Jw5?TY%4y
z0y}wwavsR-o%c(+$pR&H@7J5Q^71r;i2g&@g0W4@w{X+2tq(fAF*_5g=X${)zFkFF
z>C5`RhiABG?cogYwgW>&YoAu8tmN}?j5*D<ulN-*Zy}<>-n$9@mF_iFxn-br7gh@~
z-^Z>0!(lSBM;Hyc;a8(4P&dI<Lk3NFfLzDcj}Q!rG}nGY<C8$ZrX0o9!s~SvsVTq6
zVgwi!5w1y+$fMsa;*{V;;IDPFawPfL+>93(lw050<{p55Ps<|u&XxSB_ccR;B0OLD
z7?hr;`PEl5Q_R~Cu1sDhhjXUk#}Cql5`&D_7->+D3oVx#F~Snt2O8qmv;)zQ9iNV_
z;#N9gal8T{R$n$UIQg7$B3dw`+K6HZ0Mv53N=0C=Unlcq3qmos(cz(7DZ^iAgZtKT
zJrr2ih{FsEc~NUJ2efHTo~aVhz%v(j@mRkJRZG=v7H>@MXJ-oozZqF)JfG7hE9u1Z
zzj$=gDr*0F7r=Yyo#NF+6@#lN;Md;#QdyiRaSF``z>e!@qMVtdD}UgmoBzzc-pA44
znQq)S5jK*tp&;$tq@Bqe&S|DdQNd-a`S-sLjcltZaLd84Xa0ROBv~pm^N;4RGV;-)
z=Y0S6uK*W*KAd<Ee{dZN<%?^;Gj-&52UC9RmTWTi&H`VQBzXG?o6V>36nbkAZKz<^
zBcLG7W8NGle}^(n;X=rqEhl?7Y2C226rJjTHN%K`ws{4k?9x3|E{9Cm!u_EZxF5$j
z4nOSzHhtMzuhYsI2w?eNOYlKq8kV999{g3~BWRsa+Jp{j4UIUf$c(RBE#GdVQeZ*U
zh#yA;&TkHpx2_>^6itl@{hX}2VodsPghYof$sA6TIpZcxC=E9>0YOmdUgr)fJ>h1J
zR!xFp9dO;VNPcuA>f*=*9t;ubXX=I)rv2M#N?XTkmlNUn?Cri3z7l&29<M1F^wa`*
zOBTUG3wU43n13fd3y1gmrwLqI@1B-gI~)muo(6@&=cpF_LJ^2192>Y~S@p01-eJ2C
zUIPeF2F9&qS!+#Q)@H<cFvBC48aiSg1(x%u7F`OB=yUkNQJ3`fw2LP^ri#zfG1CBv
z`o-*~Owosg>Y7p<lp}+F_j~J}3)PPEKRT$0O5-J1HANBSq}_-pkYgrPgT|`U%L;48
zblpkm!#_xd>R9Huar65RsMs+fj%$H-1Ial)a?BFv+nLTo{qDz9*fRd+pwYkR7Qj`|
z-&6skfr;ngB9EC2bUAd$eu3m3ofM!X05v>pIZ6|uEr+<McU~NGub4L;pIk)Kw8lT|
z({-xs0`GAQ+Y)}S{73Jo>YF2~^pvxH9Slv)h=3GqT5omr5K4o)-2=wt&ueK*d+=1@
zGbkeeSj1ajuyR}Wp`f~|W97X05P1e~Jd>5A)9sToJ3r+>=r63V3nJ#a0VyfG;^5RD
zzHJ9<?J(-VcG~1So&3;(l)Nqpi+*lM5oW(EEM+Pd!IgTN9{Fq#!W)TV0ExG8#O=Qc
zy_e2LbMV9|Sk%7mdFr}*05I|+WF2!C+E8OBk;uyiQnhH_=|VXJI_8`>FkDrLb~nmh
zG2pT1qTUSOBdBesVC06O1cWfW^|wArpL!xkr-?1k=xMT&kNEc%a3iJ&g*Oze$|UYS
zP4&y(>C6f23oYzOwyA-v`uG4J)^_Yy@oAA#_h{0zzw&c&x8BHkGfH|3K7mC70=r``
zqFKM9uczq_3{_Yw6>^+Psp-ox=R6KwH~5Q&UtreATgw>LQX&F@)D47$h`#PmM@EVs
zPyT_mu;j`#Gq%}{h|JilJmGyQ{`U-QEg8M{zvKr$tE)%;@0D*$D2oRKP67&K`&^wN
zpEY=kT03h)VXRCv-|%HA?(zdF(aS_P?)nV=!Q{+8NjQdS$otqc7)_7E_e!r=6KB(>
zj2x0Xxvt%F_Kdba^&rG2-dZ^eg%_YaIIKzFbXH$z&@16GRlj5B?+IOyZXdNjU7ove
zi|o*2dW+o8$3+%4WpY9>I@E-y-_;sjWm*5d4X2!eu%UubwC-V<aH?N3Gj;ZA2r*Du
z`$Il45|n+HW%!Bi(k<&D;=^<I>zsF(?x3OwZPTg4(Xw%+kqQWCq%v{i4`htOqvb@7
zEotZU{cU+q{>RPK%h9rx{KJ~qnybUme-)0b2{me)iCNv4H7Ja@7)y9xJhtw8?ijzK
zq3txa;SaN#(Q`pEK7k$SpPV+LlFr$JPw$FyHhtFOaK&X5AJ76vj)%{KyZA&F(FiRR
zaFvvp?28>->0*bPAQD<-zm<4WQZ~fAr$a%%Is7ni);?iy*ACwGFjb?jog%zEg;GO}
zK>yRY(92`O4||1yY#v@lKxW4Zl`9x{Z#J81e4%oWg(T_lG}(PNDBSa-9sZP+mnQ_E
zsKFT-?r`uZMCj%aU3%iJE(g{3pLF#;89EB1X_bW<TVkSQ6Kn;L()fx+--P0xwT}}P
z_V#<d+N-HFGNVz~|BaAa-ucF6;(6w0l>a|@s8djSRf;WNxKRNH&=75N-B6gj&v#$&
z4nDX<<VWoZ>DXuK-b*s6hZRSseA28uVU*<bmWp!7;S8oa&u7hFGDaZatNas_WU3Lx
z2~N9XS!n+h6I65u#P@VMs|o5mGdgziH-$+vSw)_US*goZ`&(T$^*oN*IME?_?{<<i
z+c&Sb=OrLPn}~bH-%Z`D-*Do12#ok8<2J((yA1iUKlmQ=ajgdSWdlUtoMAp7xh{qG
zGgzdmj$(o;IhiTRPKbGm4Ka%v$7{K3|M;vx@Q5>dS{3_8V+d#hy4h<`fp5%*t1Hwx
zKcsRZ^zVjq-lj}d_mc#x_fN~4U~!cL4jJdR7?_i!F77!GKf?8F2Sh&-RgXc=F%f#E
zq7Vl3`1m08l{*&+c&3#f=QQI?apgf|I}*1j%hS%p%2V74Rv(IB=Mw6>kM2D~e39n1
zZ3S{1FDuYM-&xy85L_-@JiyB!ez9=(g!3%qcE6IUBwTFkMLqo$K9}f&wvqk_68zrj
z)y?Wz0?v6}UmRYTE$0FrJR4bfcQsEmPNPX7FVHg9={!aRIjD@bRDp8QQza0@|CE+4
zrYl53dwcqUS=rn3j0~eEJa}V_X5YDhxtE6QESGr`-r6NRMG_tiDs}Z@jl9?s=Wfi$
zpKRZYKNbBs!tf_=yQm?YA(C607SEkQ3?Sz0BAJr&Z}(k$j{QveoXa@>Lf!8`&W0vV
z<RBFFe8ML6xWcwd8j{IN8aMRas%G}P1IXY(O$7DB&?E?IKD?L;cm#-)p=RXA5Kp9k
z|DG@9=!+F^&TA!>>`<`0))41^#kPb{;xZ+B4;j3FPGa-qgQbJsvUL!Nab$&-YiiTw
z$?)7b`8@BuGyXQ!8TymBj$nKpS0qsCHK4$0vk-!Gh}|Zj_`Kh>UX4KcrqX+UHkT8V
zFZP4KJLy9Q0%&%YZX`S@Rt8w08h4=zLIC>)%IDuCzjA`L>7b3vl+hL~_K;>ipY+fY
z>bId=W9l-RH_y3E=quBp0gMXmGP7iM!DfQ7qAnFZFYP$>C%xAxBT2YyOc*^=F<a|w
zUV=~iR=52nSb7V8FhMLW!GoYehn}fwq^>zRh-qxM^RE48QV6b&$6Kd^OoJ)A37bBx
zVz<Z$M%#k7mM(Bg@>1@dC%v2U!F_dtC+<iO>z_1PY%Fkzz#?#3h`tn8Hr^8*D_xct
z9)?4n`u4?y62zo4OyC?<+7;Cj%Zn!_uS$`74=U4<3g+ndJk`uZZTNgjP=S}>R2EKK
zv?F!RL~cZc;{P?*VuiUKb+tUU<Mf1IHtXTwu6#2WPg_k2G@Bu~$`$@2={*|k&>I}T
zfz*?_;Tv&n=mio#q-ERiBSQi#-vmnw1-qyRl{2J}c}V5f6X9=TY(&YwY_6m?wa75<
zq#KQ<0MofMNKj!p|BovdF44^>BYuCd6(7&YD;Yf?gMsYtX!mR_h-yOur%uIt_r#~c
z3RPnRAa&2N4m1E=7Ldaf81>t5NR0Kz-{woDFe--lKi@yICnv=JL<P?%v?}lt{3Cx<
zi*l976U0@MU#S@H?9BhYCp6)Fal?zu&wViWv__u>N(o<U*xs|f%&mFmHrO5=+dDU5
zZ47&fhf8z!*T04rkN?IQ^6UC!PlTQ*>{mBJ9LV`)3xTSuzK=vlhbB6^)9v~=LUNb7
zX##h7HdGHNB+I`#vbXv1G(0TI&ZM8}1-zjzjWw?-F6r4pbP34*!h8<a+Eea(cZlY_
zGQ95Bzvj95R%F^&c%WU5mHq@rNewR%1I(q0n;&ogS~|M?9H20lsmEJGP|%VBcNh@1
z447Wd8Iz15hAGB8w8v8X7Vir8RJ_AA^8B0`lYs<LRcnagr*GnB9;{ZN+cW5-Dr#1F
zKRK_C6u2-YwS=Lhag4)N4A$SA>NQV;jdK<VnTNoi-;f=j-;xRTw_uU|5z50G#wVf;
zsp~0e9af?<`i%Em*6E8gPXo7bvaXM<6n>&$wl|GAD;;Vjhf7oFNVlP1t|L+}w(Yf{
z<-7E@fHK)6&V*r(^^*bN?CNu))E<lY>(Fq`b(kcdBH_Tr4g#S%T#|~UTD?wr+PZ4!
zpNynsO^9(6#;+!fD3!MvVq0<A+;{qND}=I)L-TG#8|7xTHEEQVHgL5c420!e8eG$6
zciBds%(ID~f3Mlvx$gy1h)k~xJ@)H`+&-Y~Vb^|Vgle|r)cVA;b>dJ_F&&O!F%1M8
zxtZ7~t0|oN<dri7mDiU?(w9qrDo3T4eDzILh7O;aJQb{EVC>?^`8qS|&+(rh7UIVn
zMIq2<_X_yF>V?h1rp=JvQc@B<;AmR2?`b(jy+$=NbKxogD-4!2;U6b)sh5dN)N^Uv
zgP{FZe(dbtYUp{Akf^A8?$t{qyFGVRm<&^R*zCi-B{E#RqP4Kxl?tZKOtkwSO1bAM
za^7RjWw`$a4g4t~)<Ro&h3dwHAgCU!$C!7bElN2-3tU*)N!Z$jZe4Qo796B%+1g7z
z7D#8zZ0ta>o>|`FGSs1uKUGLjrQsniG?I{@PrL`Ou!q>%rEZO}i~cOklP1CJCiI5w
z_#qS(!>J=d{W7#FdLyAgC8vK?WmuR5Cq}@NNZQO&5uyY)k-p%_#yxhDxgW$6S@kW@
zXyv9HdGFqx`U-O>M7&5GIu;)wyz4&u#Zq&4qt6c)Sbk13(&p^%jm8Cja{AC0H*@ra
zhjKw*>|}*GF=%#S&$eb?a#QX)k^(Fx0;kWG*HItk0n118j}-_vMC;N~sV2|HQvtK^
zed8rJ9qBz$d1=k8H!W*rV%$|hU+!OL#2#GM<sbz#=P0C|n?J+trf*c=@IK7%hWA~n
zSnWXN3+LYC2yRT=A&;|l-JE)%g%po8y#b;hjv)h?GyCR^UHG3j_8T<8=`znreoV+*
zcwV+>YDRavNOppxn0Rpho^feaM+zWtOIu^O6yyh+A28jlRkztAH%erv!R7wrGQo$K
zI0=g9F5UW$97%!I`nF3$wyHQpm<p}tj4r&(xEkK`V!a=FD!NEFyHlAO*F+MWitaXe
zjaefp3ut~2;arqiSxG<4yuQoyd@(P9+fhf3+~F4FR*L#lw=LLe&efsSh#0_GTYg-1
zw9P39s~N60pyzAiP&#T**?>mP$b*ILz90zh_V9GAa`xW&3NWLNvvtgDD4+$X1eG8~
zflo9Xh{u@;-oo`pZ5vX^Gk3&?9JR0}jt`b`d_+*+y-)6TW{h4o{)0U7f-_1bmucdq
z5D21ubS{#bE~Z8<W&$QoX5bgV!N$(V%*Msc!KTX2DZt4gz{SSM#v#DQX8S@+`TtyC
cYj0|0?)m?}fGj_+0K5SBB&i@#Ep8a}f0Xk0BLDyZ

literal 0
HcmV?d00001

diff --git a/dlls/shell32/resources/info.svg b/dlls/shell32/resources/info.svg
new file mode 100644
index 000000000000..f2b489322d06
--- /dev/null
+++ b/dlls/shell32/resources/info.svg
@@ -0,0 +1,383 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   id="svg2"
+   height="272"
+   width="632"
+   version="1.1"
+   sodipodi:docname="oic_note.svg"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)">
+  <metadata
+     id="metadata47">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1244"
+     inkscape:window-height="856"
+     id="namedview45"
+     showgrid="false"
+     inkscape:zoom="1"
+     inkscape:cx="184"
+     inkscape:cy="32"
+     inkscape:window-x="466"
+     inkscape:window-y="262"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg2" />
+  <defs
+     id="defs4">
+    <radialGradient
+       id="radialGradient3293"
+       cx="23.857"
+       gradientUnits="userSpaceOnUse"
+       cy="40"
+       r="17.143"
+       gradientTransform="matrix(1,0,0,0.5,0,20)">
+      <stop
+         id="stop4128"
+         stop-color="#000"
+         offset="0" />
+      <stop
+         id="stop4130"
+         stop-color="#000"
+         stop-opacity="0"
+         offset="1" />
+    </radialGradient>
+    <linearGradient
+       id="linearGradient3297"
+       x1="15.027"
+       gradientUnits="userSpaceOnUse"
+       y1="14.936"
+       gradientTransform="matrix(1.002994,0,0,1.002994,-0.071859,0.01968356)"
+       x2="19.015"
+       y2="31.885">
+      <stop
+         id="stop3692"
+         stop-color="#fffeff"
+         stop-opacity="0.1"
+         offset="0" />
+      <stop
+         id="stop3694"
+         stop-color="#fffeff"
+         stop-opacity="0.1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3306"
+       x1="36.918"
+       gradientUnits="userSpaceOnUse"
+       y1="66.288"
+       gradientTransform="matrix(0.920488,0,0,0.920488,10.36853,8.9735945)"
+       x2="19.071"
+       y2="5.541">
+      <stop
+         id="stop2893"
+         stop-color="#204a87"
+         offset="0" />
+      <stop
+         id="stop2895"
+         stop-color="#3465a4"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3421"
+       x1="21.994"
+       gradientUnits="userSpaceOnUse"
+       y1="33.955"
+       gradientTransform="matrix(0.68106924,0,0,0.68120249,-0.84509344,0.08898907)"
+       x2="20.917"
+       y2="15.815">
+      <stop
+         id="stop2911"
+         stop-color="#fffeff"
+         stop-opacity="0.2"
+         offset="0" />
+      <stop
+         id="stop2913"
+         stop-color="#fffeff"
+         stop-opacity="0.1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       id="radialGradient3431"
+       cx="23.857"
+       gradientUnits="userSpaceOnUse"
+       cy="40"
+       r="17.143"
+       gradientTransform="matrix(0.713704,0,0,0.175,174.40483,45.999405)">
+      <stop
+         id="stop4128-8"
+         stop-color="#000"
+         offset="0" />
+      <stop
+         id="stop4130-7"
+         stop-color="#000"
+         stop-opacity="0"
+         offset="1" />
+    </radialGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#radialGradient3293"
+       id="radialGradient872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.5,0,20)"
+       cx="23.857"
+       cy="40"
+       r="17.143" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3306"
+       id="linearGradient874"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.920488,0,0,0.920488,10.36853,8.9735945)"
+       x1="36.918"
+       y1="66.288"
+       x2="19.071"
+       y2="5.541" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3297"
+       id="linearGradient876"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.002994,0,0,1.002994,-0.071859,0.01968356)"
+       x1="15.027"
+       y1="14.936"
+       x2="19.015"
+       y2="31.885" />
+  </defs>
+  <image
+     id="icon:16-4"
+     xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAAXNSR0IArs4c6QAAAC1QTFRFAH8A AAAAf38AAAB/fwB/AH9/wMDAf39//wAAAP8A//8AAAD//wD/AP//////McQmvgAAAAF0Uk5TAEDm 2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH2QsXFCUQrZByXwAA AEtJREFUCNdjYGBgFBQUYAACRvHy8kIQS7zY2Ni8ECgApIEsATAjGcQQNwYxjAvBDGMowwwmYmaM zrADM4Da7ZJh5oANhFuBsBTqDAC5Vxf8tA357AAAAABJRU5ErkJggg== "
+     height="16"
+     width="16"
+     y="252"
+     x="608" />
+  <image
+     id="icon:16-8"
+     xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAAXNSR0IArs4c6QAAAS9QTFRFmqi8 IUuII06KJ1CNJ1GNJlKPJlKQJlOPJ1OQKVSSK1WRLVeRLFeULleSLViSLFiVLViTLViULVmVLVqV L1uXNV2UNV2VNl6VNGWkNWakNmalQ2iaRGibRWibP2qjP2qkRWqbRmqcRWykQm2lRW2kQG6pQW6p SW2eSm2eRG6mQW+qQm+pQm+qQnCqQ3CqRXCnRXCoRHGrRXKqRXKrSHKqR3OqR3OsSnWsTHatT3es UHmvUnmuUXqvUnquVH2yV3+zWIC0WYG0W4O1XIO2XYS2XoW2XoW3X4a3YIa3YYe4Y4m5ZIm5Zom3 ZYq6Zou6Z4u6Z4y7ao68bZC9bpG+b5K+cJK/cZO/cpS/gp/Cmqm8nKq9nau9nrPNpbjPrL3StMPV vMnZzMzM1dvj5efo7u7smZSEXwAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsT AAALEwEAmpwYAAAAB3RJTUUH2QsVCgQgu2cR4QAAAMFJREFUGNNjYAABRRZGJmYNBhhQF7D2Dw3x thSUgfDFlcLNpCQkpIwD9cXB8iq+QC4ISLkayAIF+MJAfO0kkIifCAODgq0ZSFYtHkTqWUgzcPlB NYA1OXEzcASDWDYJMWARTw4GziAwKy4CTHmwMvB6gbUk+YC1OHAxyFgZAlm6KRYgAS0ToNuEA4BK jJJjQQpceIDukNV0gjnMTk4B5FRRZS8doJCkqrO8GMQzskLmju5u9qb8snD/ynCzsXND/AoAF9cd /1BwUZ0AAAAASUVORK5CYII= "
+     height="16"
+     width="16"
+     y="252"
+     x="584" />
+  <image
+     id="icon:16-32"
+     xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAZiS0dEADQA ZQCkMkXLAwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kLDxEDKjsklVIAAAJcSURBVDjL fVNLTxNhFD3fR+dRWqhFSgMzrRRTHgopLro2cWs0StggWxONq7JxiRBduFLCRllqojshJv4KNlKD xrRCKdCHjIVK6XRm2s511xSm8e7uzbnnnNwHw4WYnF+7YxnGcqNuXaNmU2KcE8AsQZKSgiQ93/7w 6Es7nrUnY7Orm6KLxaMRBYE+L9ySAAKhWqtDO65gN1uEaTW+pdYTMQdB9P7r3LASGJoaV/A9U8JJ xYRNBADgjMHnFRFV/Ujva9jLFvPpjQWlRTA2u7oZHuyLD4cGkPyltRovBmcM41f8KGinyO4Xk6n1 xDSfml+7LboQnxxVHM293QLePb3Zym0i/MyeIKL0we2WYtfn3sxw06i9iI6o+LFXcigTAZni6bma TYSdXBkhZQB101jidtOO9vu9OKmYDsuVWh1L778663odvh4ZRBThnHO3LLoc6qGABy8fxvHk7oSD wCaC6OoCY7ybM8aIOgztQKuifGbi8E+140CJCEQEbtu2rpsNcMYcoImwH4dH1Y7bMKwmiKjGGWOZ UllHr0c8B+r1iBi87Ea+pDsIPG4B5VMdnPMdLsrys51sHleVS+dcdHGgZjYwd2vEoa4GvDjMHUGU 5UUGAKMzK1thdSAWDPiQPij/95DCwR5opb8o/C5tpT4lbrSd8qt8SA0ORtR+ZPJ/cVart4gYAzyy ADXQg4PCMfIFrZDeWBhyPNPozMqWJAmxkBqEzytDFLpABJhWA+WzGnI5DZZVT6bWE9MdvxEAJh+8 vWcZxjKRPcIY9xLIBpHOGN8VZXlx++Pjz+34fwepDdkjUQmrAAAAAElFTkSuQmCC "
+     height="16"
+     width="16"
+     y="252"
+     x="560" />
+  <image
+     id="icon:32-4"
+     xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAAAXNSR0IArs4c6QAAAC1QTFRFAH8A AAAAf38AAAB/fwB/AH9/wMDAf39//wAAAP8A//8AAAD//wD/AP//////McQmvgAAAAF0Uk5TAEDm 2GYAAAABYktHRACIBR1IAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH2QsXFCUTNJkj5QAA AJZJREFUKM+VkUEKwzAMBJMfRBd/ZCG+5S+moN56CUQPDuToN7SW1ThESaF722El7FXXXYuIhoPt iZkTNR9YRBbeSXiKim2q/3gAUSRZQGaoZBksUByXTKpAA7lEUp2AJRB1xgBuga0YH7AlFUz5BPBy AH+DbQf2ju30sNGBtQFdUhPf75dIbn24xnynrnV/F3c5f9ufegP03F+97qymfQAAAABJRU5ErkJg gg== "
+     height="32"
+     width="32"
+     y="236"
+     x="520" />
+  <image
+     id="icon:32-8"
+     xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAq9QTFRFaYKl IkyJJU6JJk+JJ1CKKlGMKVKLKlKLKlKMK1OMK1ONLVOOLVSOL1aOMFeOMViPMliPM1mPMlqQOl6S Ol+TP2KWPWOaNGWkPWSaPmSbNWalNmelQmWWN2elQ2WWP2aaRGWXQGabQGacOGimRWeWOWmmOmmn PGunPGuoQ2qeRWqePWyoPmyoRmufPm2oPm2pRWyfP26pQG6pSG2hQW+qQXCqQ3CqQ3CrSW+kSnCl S3ClRXKsTHGlTnGjRnOsTHKlTHKnR3OsUHKkTHOnR3StSHStVXKdSXWtSXWuSnWsVnOdVnOeV3Od V3OfSnauV3SeS3auV3SfWXSfS3euTHeuTHevUXeoTXivU3eqTniwTnmwT3mvW3egT3mwVHmsUHqw UHqxXXifV3mrVHqsVXqsVnqrUXuxUnyxUnyyU3yxU3yyU32yU32zWXysVH2yVH2zVX6zV3+yV3+0 W3+tV4C0WIC0XYCwWoG0WoK1W4K1XoKxXIO1XYO2XYS2XoS0aoKlYoWzbISmYIa2YIa4Y4a2ZIe2 ZYe0Zoi1Yom5ZIm6ZYq6Zoq4Zou6Z4u4aIu4aYy4aIy8aY28bo67a4+9bY+8bZC+bZG+cJK9cJK+ cJK/cZK+cZPAc5O+c5TAdZS+c5XAdJW/dJXAdJXBdJbBdpfBeJfAeZjBeprDeprEe5rEepvEfZvD fZzEf5zCf5zEfp3Efp3Ff53Ff57GgJ7Gg57CgJ/GgZ/Ggp/FgaDGgqDGj560gqHHhaHDkqC1h6PF hqPIiKPFiaTFiaTGjKXGjqfHkarIm7HNoLXOobbPprnQtbvDtrvDt7zDub3Err/Tr7/Tr8DUvsHG tMPVv8LHwMPHw8XIusjYvMnZyMnKysrLzMzMxdDcztfg0tnh193j3OHl5ejp7Ozr7O3r7e3r7u7s spFDYwAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJ TUUH2QsVCTIAC1MeBQAAAfBJREFUOMtjYCABePuKCnALifp4Y5cO5HNqn7d65+p5bba8QVh08xdv WzGhPDsyu2LCyq253OimyBhvXJjhDgPZC9fpyqHIC6TuKHdHBuU7UgWR9UdsSHNHBembklQQ9itt g8sX3j4JYWTsNoC7g205wvy1Tx5lQljVa7hgBoQtRBgdfWIfjLkkJQaigHlzhjs2kLOZBWKA1gqo iAcU+EHBBk2wK9yqJkL4AQEBId0HjgcEhELB5DJ/kALhOTUQfnh40/0nT+6Ew0BU8yQRkAL29SUQ PggcenLBUFlVwzUWCOIrlvGAFDDuz48F80Hg2JMt4iCg5piQlla0ngmkgHN7ZRoMBF96UicOAcrW abULwCYIL+3IAIKsrKwMc/F7j9XFYUAqoQXsBq+G2XlAUJBlpypu9uSuOAKUxoF94a24qqCgINZE CijU9+QckoJp8pDYYJ5rqioNETry5CBC3mg+CzQu7DthYuef9CIUdNnEwGJzliNU7PqTo5792hC2 +3Q2eHqQWKQHEdzz8MG1wxB/6C8WQ6RLGcuZOlB7ZaFm6c+0UkFOk5bzHcWRgftiCwGUVKsgMaPL CC5t0jNLUgU9X3A4z59a4mKoZuRSMm2uAyuW3BXDopTcOmXhlNZkJeZEHHnTW5idkVPY35uBqgAA lFfQgymcmM8AAAAASUVORK5CYII= "
+     height="32"
+     width="32"
+     y="236"
+     x="480" />
+  <rect
+     id="icon:32-32"
+     y="236"
+     x="440"
+     width="32"
+     height="32"
+     style="fill:none" />
+  <image
+     id="icon:48-4"
+     xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAAXNSR0IArs4c6QAAAC1QTFRFAH8A AAAAf38AAAB/fwB/AH9/wMDAf39//wAAAP8A//8AAAD//wD/AP//////McQmvgAAAAF0Uk5TAEDm 2GYAAAABYktHRACIBR1IAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH2QsXFCUV3fqG0AAA AOJJREFUOMullEEOhCAMRfUGsuEiTZyddyEmzM6NiRx4EpecYYpIKWDHmPkbk770f8HWrrtVr1BD W1faooxq6vbtUJtVdd2dKklPdSQ8R4cKoMLTsAb0X+HQC3MG3nDWD2JYA9UDoRYd/QHGOeaYumHZ ixYEwAEkQE6nFXlpHh29TPtOLCRHkG5AzgabwBFC2db7Ip2B/RrA9Bgsn6fAi2CugKtBPCBdyeiF u7oAMWTy1Tik9Op8OZ1A+rQpJIM8JWvM8OX4JK/RlkPyY0TFoZbXQFwcedXE5ZTXWf4ByL+M//UF bizd0VWSQrQAAAAASUVORK5CYII= "
+     height="48"
+     width="48"
+     y="220"
+     x="384" />
+  <image
+     id="icon:48-8"
+     xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAAAXNSR0IArs4c6QAAAnlQTFRFMliO Jk2IJ06JIlSPJFSVI1WQLlKPJVWWL1OQLlWKJ1aXMFSRHFqaKFeYMVWSJ1iTHlubMlaTMVeNK1ia IFycKlqVLFmbM1mPIV2dK1uWLVqcLFuXNVqQI16eL1udLlyYJF+fL12ZMF6aMV+bM2CcOl+WNGGd O2CXNWKePGGYNmOfNWSaPGOTPmKZOGSgPWSUP2OaRmKUOWWhQGSbOmaiP2aXO2ejQGeYPGikQWiZ RGefPWmlPGqgRWigP2qnPmyiQGuoR2qiP22jSGujQW6kSmykQm+lS22lQ3CmTG6mRHGnUG+cRXKo RnOqTXKkR3SrSHWsVHOgT3SmSnatUHWnS3euVnWiUXaoTHivU3epWHekTnmxVHiqWnilVXmsW3mm VHunUHuzVnqtXHqnV3uuXXuoWHyvXnypWX2wXX6lZHylX32qWH+rWn6xYH6rW3+yYX+sWoGtXICz Z3+oYoCtW4KuXYG0Y4GuXoK1ZIKwXYSxY4SrZYOxXoWyZIWsX4azWoi5Z4WzYIe0ZoeuYYi1Z4iv aYe1Yom2b4ewaYqxZYu4aouyc4qna4y0Z426bI21aI67c4y1bY62aY+8bo+3b5C4dJCycJG5dZGz cZK6dpK0cpO7d5O1eJS2dJW9fJSxeZW4gZSsepa5dpe/e5e6d5jAfJi7fZm8fpq9f5u+gJy/gZ3A gp7Bg5/ChKDDhqHEh6LGi6LAiKPHjKPBiaXIkKfFkajGm6/HnrLKobTNorXOo7bPpbjRprnSq7rN scDUssHVtcXYvsrYwMzayszJws7cxNDextLgzNXdztff0tri09vk2+Dj4ufq4+jr5+nm6uzp6+7q 7e/r1KigVQAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAA B3RJTUUH2QsVCToTRzTV0wAAA2NJREFUSMfFlulPE1EQwKUUymELbhEaQJAi27RGK0pACJZ0A+kG GgMWiNUoKmC4FIhVjAareCCm9QBJuGo2QSuipFDjfWs8IAg6f5Gv+3q83Rbkg4m/T/3wm+nM7Nt5 u27dP8BRo6ckEkpf41iL3Rq3rXvQ7Xn+zDM52KGNa/2LXqzp9XjHL3YetViOdl4c93p6NcWr6FWZ /d6hFkuI6uND3quZVSv5cd3eobqQjLHcnu2Ki+ynjU4eFNlmH/vvjaZF0Nu2Tt+MYPPceKRvCwvY OnM2oszTOa0Pq2fqzEo2H/GQEvU73C+S6zzeOiKibzheMM+uIXHqtwAfAr8rEYMd5HQzpy3iQn4A LAZthHmKGFWx/VJY1bO/fj8LyJUsoudc6JlrHkXos+lESOZxa4LnzWYX22RqHyaTie0+GTiJMQ/3 r2abfDaikovxB2gfrJ7aR0VFBcP5a3I0XgmzzdeevmsJpEZyuY+eQ/iNqrncQtg+49W3ZYDf1QIb se9CDR+gv3tYVMj770sACyxjMBjKCdhb+ERR981hPdoBPuo2IdS63fgfGIbZw+EDJXnChvU4AzCH 9KwslUqlLmQw9JQEBzw2Ccbnq/oNgJ23Var09BQ+xGhU+wMoziy0yxndV4BSv82TW2I0ltIuXJJ+ sE44vkL15mVYRDK2U1OTETl0aokzH4/10jFyfEZapaoF+IRT83JysnI9gj2Px+poOG0IDrtMl4MK GQV4HkjttxHtB/yrUMNt8Y+ukMZVvwBwCm0lYjRwXGO4giy6YKeOzgn0+AWAJVIreWhXbOB4tzfx Awn2mLIEPzOUpKxUyuXW5uCi1bjV6WSPewE+i2y5PIPThF7RnqYUssc+gNdBW45JstqKiK10P5fs cQIdPVq5vTtkJyRkc+S+rGq2kz2yAPBrEeaxncBzrkGwxOPvWMke5xbmX94qwal9KBS1zmjh6qNc jLDHYOoEhUwmKx2jxMtVz5UJeiRs5Lu04eteO2EV2ViWJSbuG9O2Rbghkpy92cJCeFua0XOdinwF RR/hrBsJWZYolUo3WF2Hole8FNNsLqs2mBqRWzvRnla1yj1apGmcGGlmdmUrFNk7yhpHxhryiv52 scfm1dsGRtzukYFT9XmxrWv7dMinoqKo/LV9Ovx//gDtqJRBNXNLfgAAAABJRU5ErkJggg== "
+     height="48"
+     width="48"
+     y="220"
+     x="328" />
+  <rect
+     id="icon:48-32"
+     y="220"
+     x="272"
+     width="48"
+     height="48"
+     style="fill:none" />
+  <rect
+     id="icon:256-32"
+     y="8"
+     x="8"
+     width="256"
+     height="256"
+     style="fill:none" />
+  <g
+     id="g3310"
+     transform="translate(264,212)">
+    <g
+       id="layer2"
+       transform="translate(7.9999996,7.9995145)">
+      <path
+         id="path6548"
+         style="color:#000000;display:block;opacity:0.6;fill:url(#radialGradient3293);fill-rule:nonzero"
+         d="M 41,40 A 17.143,8.5714 0 1 1 6.714,40 17.143,8.5714 0 1 1 41,40 Z"
+         transform="matrix(1.070555,0,0,0.525,-0.892755,22.5)"
+         display="block"
+         inkscape:connector-curvature="0" />
+    </g>
+    <path
+       id="path1314"
+       style="fill:url(#linearGradient3306);fill-rule:nonzero;stroke:#204a87;stroke-width:0.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+       d="m 53.5,31 a 21.5,21.5 0 0 1 -43,0 21.5,21.5 0 1 1 43,0 z"
+       stroke-miterlimit="4"
+       inkscape:connector-curvature="0" />
+    <path
+       id="path3560"
+       style="opacity:0.45;fill-opacity:0;stroke:#eeeeec;stroke-width:0.99999887;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+       d="m 52.539,31 a 20.539,20.539 0 0 1 -41.077,0 20.539,20.539 0 1 1 41.077,0 z"
+       stroke-miterlimit="4"
+       inkscape:connector-curvature="0" />
+    <g
+       id="layer4"
+       transform="translate(7.9999996,7.9995145)">
+      <path
+         id="path3955"
+         d="m 43.429,21.8 c 0,10.863 -10.385,-6.2851 -18.731,0.38754 -8.1507,6.517 -20.659,12.227 -20.659,1.364 0,-11.117 8.7214,-21.431 19.584,-21.431 10.8626,0 19.805,8.8164 19.805,19.679 z"
+         style="fill:url(#linearGradient3297);fill-rule:nonzero"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       id="text3046"
+       font-size="40px"
+       transform="translate(7.9999996,7.9995145)"
+       font-style="normal"
+       font-weight="normal"
+       style="font-style:normal;font-weight:normal;font-size:40px;font-family:'Bitstream Vera Sans';fill:#eeeeec">
+      <path
+         id="path28492"
+         style="text-align:center;fill:#eeeeec"
+         d="m 26.05,10.202 c -1.08,0 -1.944,0.864 -1.944,1.944 0,1.116 0.864,1.944 1.944,1.944 1.044,0 1.944,-0.864 1.944,-1.944 0,-1.044 -0.9,-1.944 -1.944,-1.944 m -0.72,7.992 -5.328,1.152 v 0.936 l 2.916,0.54 -2.124,10.728 c -0.144,0.792 -0.216,1.44 -0.216,1.872 0,1.584 0.756,2.376 2.304,2.376 1.368,0 3.096,-0.432 5.076,-1.26 v -1.116 c -1.116,0.36 -1.98,0.54 -2.664,0.54 -1.116,0 -1.728,-0.468 -1.728,-1.404 0,-0.216 0.036,-0.504 0.108,-0.9 L 26.266,18.194 H 25.33"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+  <g
+     id="g3436"
+     transform="translate(264,212)">
+    <path
+       id="path6548-4"
+       style="color:#000000;display:block;opacity:0.6;fill:url(#radialGradient3431);fill-rule:nonzero"
+       d="m 203.67,52.999 a 12.235,3 0 1 1 -24.47,0 12.235,3 0 1 1 24.47,0 z"
+       display="block"
+       inkscape:connector-curvature="0" />
+    <g
+       id="g2915"
+       transform="translate(176,23.999395)">
+      <path
+         id="path1949"
+         style="fill:#3465a4;stroke:#204a87;stroke-width:1.00000072;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+         d="m 29.486,15.496 a 13.993,13.996 0 0 1 -27.986,0 13.993,13.996 0 1 1 27.986,0 z"
+         stroke-miterlimit="4"
+         inkscape:connector-curvature="0" />
+      <path
+         id="path3560-8"
+         style="opacity:0.34659089;fill-opacity:0;stroke:#ffffff;stroke-width:0.99999946;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+         d="m 28.422,15.496 a 12.929,12.931 0 0 1 -25.858,0 12.929,12.931 0 1 1 25.858,0 z"
+         stroke-miterlimit="4"
+         inkscape:connector-curvature="0" />
+      <path
+         id="path3955-0"
+         d="m 28.693,14.882 c 0,7.3778 -7.052,-4.2686 -12.719,0.26321 -5.5346,4.4261 -14.028,8.3042 -14.028,0.92639 0,-7.5505 5.9222,-14.555 13.299,-14.555 7.3764,0 13.448,5.9878 13.448,13.366 z"
+         style="fill:url(#linearGradient3421);fill-rule:nonzero"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       id="text3046-4"
+       font-size="40px"
+       transform="translate(176,23.999395)"
+       font-style="normal"
+       font-weight="normal"
+       style="font-style:normal;font-weight:normal;font-size:40px;font-family:'Bitstream Vera Sans';fill:#eeeeec">
+      <path
+         id="path28468"
+         style="text-align:center;fill:#eeeeec"
+         d="m 16.861,6.9638 c -0.72,0 -1.296,0.576 -1.296,1.296 0,0.744 0.576,1.296 1.296,1.296 0.696,0 1.296,-0.576 1.296,-1.296 0,-0.696 -0.6,-1.296 -1.296,-1.296 m -0.48,5.328 -3.552,0.768 v 0.624 l 1.944,0.36 -1.416,7.152 c -0.096,0.528 -0.144,0.96 -0.144,1.248 0,1.056 0.504,1.584 1.536,1.584 0.912,0 2.064,-0.288 3.384,-0.84 v -0.744 c -0.744,0.24 -1.32,0.36 -1.776,0.36 -0.744,0 -1.152,-0.312 -1.152,-0.936 0,-0.144 0.024,-0.336 0.072,-0.6 l 1.728,-8.976 h -0.624"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+  <g
+     transform="matrix(5.33,0,0,5.33,-34.300001,-34.477332)"
+     id="g870">
+    <g
+       transform="translate(7.9999996,7.9995145)"
+       id="g856">
+      <path
+         inkscape:connector-curvature="0"
+         display="block"
+         transform="matrix(1.070555,0,0,0.525,-0.892755,22.5)"
+         d="M 41,40 A 17.143,8.5714 0 1 1 6.714,40 17.143,8.5714 0 1 1 41,40 Z"
+         style="color:#000000;display:block;opacity:0.6;fill:url(#radialGradient872);fill-rule:nonzero"
+         id="path854" />
+    </g>
+    <path
+       inkscape:connector-curvature="0"
+       stroke-miterlimit="4"
+       d="m 53.5,31 a 21.5,21.5 0 0 1 -43,0 21.5,21.5 0 1 1 43,0 z"
+       style="fill:url(#linearGradient874);fill-rule:nonzero;stroke:#204a87;stroke-width:0.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+       id="path858" />
+    <path
+       inkscape:connector-curvature="0"
+       stroke-miterlimit="4"
+       d="m 52.539,31 a 20.539,20.539 0 0 1 -41.077,0 20.539,20.539 0 1 1 41.077,0 z"
+       style="opacity:0.45;fill-opacity:0;stroke:#eeeeec;stroke-width:0.99999887;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+       id="path860" />
+    <g
+       transform="translate(7.9999996,7.9995145)"
+       id="g864">
+      <path
+         inkscape:connector-curvature="0"
+         style="fill:url(#linearGradient876);fill-rule:nonzero"
+         d="m 43.429,21.8 c 0,10.863 -10.385,-6.2851 -18.731,0.38754 -8.1507,6.517 -20.659,12.227 -20.659,1.364 0,-11.117 8.7214,-21.431 19.584,-21.431 10.8626,0 19.805,8.8164 19.805,19.679 z"
+         id="path862" />
+    </g>
+    <g
+       style="font-style:normal;font-weight:normal;font-size:40px;font-family:'Bitstream Vera Sans';fill:#eeeeec"
+       font-weight="normal"
+       font-style="normal"
+       transform="translate(7.9999996,7.9995145)"
+       font-size="40px"
+       id="g868">
+      <path
+         inkscape:connector-curvature="0"
+         d="m 26.05,10.202 c -1.08,0 -1.944,0.864 -1.944,1.944 0,1.116 0.864,1.944 1.944,1.944 1.044,0 1.944,-0.864 1.944,-1.944 0,-1.044 -0.9,-1.944 -1.944,-1.944 m -0.72,7.992 -5.328,1.152 v 0.936 l 2.916,0.54 -2.124,10.728 c -0.144,0.792 -0.216,1.44 -0.216,1.872 0,1.584 0.756,2.376 2.304,2.376 1.368,0 3.096,-0.432 5.076,-1.26 v -1.116 c -1.116,0.36 -1.98,0.54 -2.664,0.54 -1.116,0 -1.728,-0.468 -1.728,-1.404 0,-0.216 0.036,-0.504 0.108,-0.9 L 26.266,18.194 H 25.33"
+         style="text-align:center;fill:#eeeeec"
+         id="path866" />
+    </g>
+  </g>
+</svg>
diff --git a/dlls/shell32/shell32.rc b/dlls/shell32/shell32.rc
index fe80339568bd..6738ba802dc0 100644
--- a/dlls/shell32/shell32.rc
+++ b/dlls/shell32/shell32.rc
@@ -590,9 +590,6 @@ IDI_SHELL_BLANK4 ICON resources/blank.ico
 /* @makedep: resources/drive.ico */
 IDI_SHELL_UNKNOWN_DRIVE ICON resources/drive.ico
 
-/* @makedep: resources/shield.ico */
-IDI_SHELL_SHIELD ICON resources/shield.ico
-
 /* FIXME: Following resource is not yet added */
 /* @makedep: resources/document.ico */
 IDI_SHELL_FILES ICON resources/document.ico
@@ -606,7 +603,7 @@ IDI_SHELL_FILE_SEARCH ICON resources/search.ico
 IDI_SHELL_SYSTEM_SEARCH ICON resources/search.ico
 
 /* @makedep: resources/control.ico */
-IDI_SHELL_OPTIONS ICON resources/control.ico
+IDI_SHELL_CONTROL_PANEL_XP ICON resources/control.ico
 
 /* FIXME: Following resource is not yet added */
 /* @makedep: resources/folder.ico */
@@ -643,7 +640,7 @@ IDI_SHELL_FOLDER_OVERWRITE ICON resources/delete.ico
 
 /* FIXME: Following resource is not yet added */
 /* @makedep: resources/rename.ico */
-IDI_SHELL_RENAME_FOLDER ICON resources/rename.ico
+IDI_SHELL_FOLDER_RENAME ICON resources/rename.ico
 
 /* @makedep: resources/update.ico */
 IDI_SHELL_INSTALL ICON resources/update.ico
@@ -1069,6 +1066,16 @@ IDI_SHELL_RESTRICTED2 ICON resources/restricted.ico
 /* @makedep: resources/internet.ico */
 IDI_SHELL_WEB_BROWSER ICON resources/internet.ico
 
+/* @makedep: resources/info.ico */
+IDI_SHELL_INFO ICON resources/info.ico
+
+/* FIXME: Following resource is not yet added */
+/* @makedep: resources/mycomputer.ico */
+IDI_SHELL_COMPUTER_OFF ICON resources/mycomputer.ico
+
+/* @makedep: resources/shield.ico */
+IDI_SHELL_SHIELD ICON resources/shield.ico
+
 /* @makedep: resources/idb_sort_incr.bmp */
 IDB_SORT_INCR BITMAP resources/idb_sort_incr.bmp
 
diff --git a/dlls/shell32/shfldr_fs.c b/dlls/shell32/shfldr_fs.c
index fabd27e984f8..0d74f4578c5d 100644
--- a/dlls/shell32/shfldr_fs.c
+++ b/dlls/shell32/shfldr_fs.c
@@ -1409,7 +1409,6 @@ IFSFldr_PersistFolder3_Initialize (IPersistFolder3 * iface, LPCITEMIDLIST pidl)
         if (IsEqualCLSID( This->pclsid, &CLSID_MyDocuments ))
         {
             if (!SHGetSpecialFolderPathW( 0, wszTemp, CSIDL_PERSONAL, FALSE )) return E_FAIL;
-            PathAddBackslashW( wszTemp );
         }
         else lstrcpyW( wszTemp, L"\\\\?\\unix\\" );
     }
diff --git a/dlls/shell32/shresdef.h b/dlls/shell32/shresdef.h
index deabe5680227..64763f820f6a 100644
--- a/dlls/shell32/shresdef.h
+++ b/dlls/shell32/shresdef.h
@@ -227,7 +227,6 @@
 #define IDI_SHELL_BLANK3            52
 #define IDI_SHELL_BLANK4            53
 #define IDI_SHELL_UNKNOWN_DRIVE     54
-#define IDI_SHELL_SHIELD            78
 /* 55 - 132 not used on Windows */
 #define IDI_SHELL_FILES            133
 #define IDI_SHELL_FILE_SEARCH      134
@@ -375,6 +374,12 @@
 #define IDI_SHELL_RESTRICTED2      338
 /* 339 - 511 not used on Windows */
 #define IDI_SHELL_WEB_BROWSER      512
+/* 512 - 1000 not used on Windows */
+#define IDI_SHELL_INFO             1001
+/* 1012 - 8239 not used on Windows */
+#define IDI_SHELL_COMPUTER_OFF     8240
+/* 8241 - 16709 not used on Windows */
+#define IDI_SHELL_SHIELD           16747
 
 /* 
 AVI resources, windows shell32 has 14 of them: 150-152 and 160-170
diff --git a/dlls/shell32/tests/shlfolder.c b/dlls/shell32/tests/shlfolder.c
index 5f8b4dc34709..08c8c73dd479 100644
--- a/dlls/shell32/tests/shlfolder.c
+++ b/dlls/shell32/tests/shlfolder.c
@@ -1501,6 +1501,34 @@ static void test_SHGetPathFromIDList(void)
     ok(result, "SHGetPathFromIDListW failed\n");
 }
 
+static void test_SHGetPathFromIDList_personal(void)
+{
+    WCHAR personal[MAX_PATH], path[MAX_PATH];
+    LPITEMIDLIST pidl;
+    HRESULT hr;
+    BOOL ret;
+    int len;
+
+    ret = SHGetSpecialFolderPathW(NULL, personal, CSIDL_PERSONAL, FALSE);
+    ok(ret, "SHGetSpecialFolderPathW(CSIDL_PERSONAL) failed, error %lu.\n", GetLastError());
+    if (!ret) return;
+
+    hr = SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl);
+    ok(hr == S_OK, "SHGetSpecialFolderLocation(CSIDL_PERSONAL) failed, hr %#lx.\n", hr);
+    if (hr != S_OK) return;
+
+    ret = SHGetPathFromIDListW(pidl, path);
+    ILFree(pidl);
+    ok(ret, "SHGetPathFromIDListW(CSIDL_PERSONAL) failed, error %lu.\n", GetLastError());
+    if (!ret) return;
+    ok(!lstrcmpiW(path, personal), "got %s, expected %s.\n", wine_dbgstr_w(path),
+       wine_dbgstr_w(personal));
+
+    len = lstrlenW(path);
+    ok(len && path[len - 1] != '\\', "CSIDL_PERSONAL path has trailing backslash: %s.\n",
+       wine_dbgstr_w(path));
+}
+
 static void test_EnumObjects_and_CompareIDs(void)
 {
     ITEMIDLIST *newPIDL;
@@ -6291,6 +6319,7 @@ START_TEST(shlfolder)
     test_GetDisplayName();
     test_GetAttributesOf();
     test_SHGetPathFromIDList();
+    test_SHGetPathFromIDList_personal();
     test_CallForAttributes();
     test_FolderShortcut();
     test_ITEMIDLIST_format();
diff --git a/dlls/tdh/Makefile.in b/dlls/tdh/Makefile.in
index 34c97e13cdb9..f373ca151621 100644
--- a/dlls/tdh/Makefile.in
+++ b/dlls/tdh/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = tdh.dll
 IMPORTLIB = tdh
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine Event Trace Helper Library"
 VER_PRODUCTVERSION      = 10,0,16299,15
 
diff --git a/dlls/thumbcache/Makefile.in b/dlls/thumbcache/Makefile.in
new file mode 100644
index 000000000000..200c1d656f67
--- /dev/null
+++ b/dlls/thumbcache/Makefile.in
@@ -0,0 +1,6 @@
+MODULE    = thumbcache.dll
+IMPORTS   = uuid
+
+SOURCES = \
+	thumbcache.c \
+	thumbcache_reg.idl
diff --git a/dlls/thumbcache/tests/Makefile.in b/dlls/thumbcache/tests/Makefile.in
new file mode 100644
index 000000000000..90c7a7a118c8
--- /dev/null
+++ b/dlls/thumbcache/tests/Makefile.in
@@ -0,0 +1,5 @@
+TESTDLL   = thumbcache.dll
+IMPORTS   = uuid ole32
+
+SOURCES = \
+	thumbcache.c
diff --git a/dlls/thumbcache/tests/thumbcache.c b/dlls/thumbcache/tests/thumbcache.c
new file mode 100644
index 000000000000..24ff387410eb
--- /dev/null
+++ b/dlls/thumbcache/tests/thumbcache.c
@@ -0,0 +1,49 @@
+/*
+* Copyright 2026 Alistair Leslie-Hughes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+#define CONST_VTABLE
+
+#include "thumbcache.h"
+
+#include "wine/test.h"
+
+static void test_create_thumbnailcache(void)
+{
+    HRESULT hr;
+    IThumbnailCache *cache;
+
+    hr = CoCreateInstance( &CLSID_LocalThumbnailCache, NULL, CLSCTX_INPROC_SERVER, &IID_IThumbnailCache,
+                           (void **)&cache );
+    ok( hr == S_OK, "got 0x%lx\n", hr);
+
+    IThumbnailCache_Release(cache);
+}
+
+START_TEST(thumbcache)
+{
+    HRESULT hr;
+
+    hr = CoInitialize( NULL );
+    ok( hr == S_OK, "failed to init com\n");
+    if (hr != S_OK) return;
+
+    test_create_thumbnailcache();
+
+    CoUninitialize();
+}
diff --git a/dlls/thumbcache/thumbcache.c b/dlls/thumbcache/thumbcache.c
new file mode 100644
index 000000000000..088ac4717649
--- /dev/null
+++ b/dlls/thumbcache/thumbcache.c
@@ -0,0 +1,209 @@
+/*
+* Copyright 2026 Alistair Leslie-Hughes
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "oleidl.h"
+#include "rpcproxy.h"
+#include "thumbcache.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(thumbcache);
+
+struct thumb_cache
+{
+    IThumbnailCache IThumbnailCache_iface;
+    LONG ref;
+};
+
+static struct thumb_cache *impl_from_IThumbnailCache( IThumbnailCache *iface )
+{
+    return CONTAINING_RECORD( iface, struct thumb_cache, IThumbnailCache_iface );
+}
+
+static HRESULT WINAPI thumbcache_QueryInterface(IThumbnailCache *iface, REFIID iid, void **obj)
+{
+    struct thumb_cache *This = impl_from_IThumbnailCache(iface);
+
+    TRACE( "(%p)->(%s %p)\n", This, debugstr_guid( iid ), obj );
+
+    if (IsEqualIID( iid, &IID_IUnknown ) ||
+        IsEqualIID( iid, &IID_IThumbnailCache ))
+    {
+        IThumbnailCache_AddRef( iface );
+        *obj = &This->IThumbnailCache_iface;
+        return S_OK;
+    }
+
+    FIXME( "interface %s not implemented\n", debugstr_guid( iid ) );
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI thumbcache_AddRef(IThumbnailCache *iface)
+{
+    struct thumb_cache *This = impl_from_IThumbnailCache(iface);
+    ULONG ref = InterlockedIncrement( &This->ref );
+
+    TRACE( "(%p) ref = %lu\n", This, ref );
+    return ref;
+}
+
+static ULONG WINAPI thumbcache_Release(IThumbnailCache *iface)
+{
+    struct thumb_cache *This = impl_from_IThumbnailCache(iface);
+    ULONG ref = InterlockedDecrement(&This->ref);
+
+    TRACE( "(%p) ref = %lu\n", This, ref );
+
+    if (!ref)
+    {
+        free( This );
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI thumbcache_GetThumbnail(IThumbnailCache *iface, IShellItem *pShellItem, UINT cxyRequestedThumbSize,
+    WTS_FLAGS flags, ISharedBitmap **ppvThumb, WTS_CACHEFLAGS *pOutFlags, WTS_THUMBNAILID *pThumbnailID)
+{
+    struct thumb_cache *This = impl_from_IThumbnailCache(iface);
+
+    FIXME("%p, %p, %d, %x, %p, %p, %p\n", This, pShellItem, cxyRequestedThumbSize, flags, ppvThumb, pOutFlags, pThumbnailID );
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI thumbcache_GetThumbnailByID(IThumbnailCache *iface, WTS_THUMBNAILID thumbnailID,
+    UINT cxyRequestedThumbSize, ISharedBitmap **ppvThumb, WTS_CACHEFLAGS *pOutFlags)
+{
+    struct thumb_cache *This = impl_from_IThumbnailCache(iface);
+
+    FIXME("%p, %p, %u, %p, %p\n", This, &thumbnailID, cxyRequestedThumbSize, ppvThumb, pOutFlags );
+
+    return E_NOTIMPL;
+}
+
+static const struct IThumbnailCacheVtbl thumb_cache_vtbl =
+{
+    thumbcache_QueryInterface,
+    thumbcache_AddRef,
+    thumbcache_Release,
+    thumbcache_GetThumbnail,
+    thumbcache_GetThumbnailByID
+};
+
+static HRESULT thumbcache_create( IUnknown *outer, REFIID iid, void **obj )
+ {
+    struct thumb_cache *This = malloc( sizeof(*This) );
+    HRESULT hr = E_FAIL;
+
+    if (!This) return E_OUTOFMEMORY;
+    This->IThumbnailCache_iface.lpVtbl = &thumb_cache_vtbl;
+    This->ref = 1;
+
+    hr = IThumbnailCache_QueryInterface( &This->IThumbnailCache_iface, iid, obj );
+    IThumbnailCache_Release( &This->IThumbnailCache_iface );
+    return hr;
+}
+
+struct class_factory
+{
+    IClassFactory IClassFactory_iface;
+    HRESULT (*create_instance)(IUnknown *, REFIID, void **);
+};
+
+static inline struct class_factory *impl_from_IClassFactory( IClassFactory *iface )
+{
+    return CONTAINING_RECORD( iface, struct class_factory, IClassFactory_iface );
+}
+
+static HRESULT WINAPI class_factory_QueryInterface( IClassFactory *iface,
+                                                    REFIID iid, void **obj )
+{
+    if (IsEqualIID( iid, &IID_IUnknown ) ||
+        IsEqualIID( iid, &IID_IClassFactory ))
+    {
+        IClassFactory_AddRef( iface );
+        *obj = iface;
+        return S_OK;
+    }
+
+    FIXME( "interface %s not implemented\n", debugstr_guid( iid ) );
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI class_factory_AddRef( IClassFactory *iface )
+{
+    return 2;
+}
+
+static ULONG WINAPI class_factory_Release( IClassFactory *iface )
+{
+    return 1;
+}
+
+static HRESULT WINAPI class_factory_CreateInstance( IClassFactory *iface,
+                                                    IUnknown *outer, REFIID iid,
+                                                    void **obj )
+{
+    struct class_factory *This = impl_from_IClassFactory( iface );
+
+    TRACE( "%p %s %p\n", outer, debugstr_guid( iid ), obj );
+
+    *obj = NULL;
+    return This->create_instance( outer, iid, obj );
+}
+
+static HRESULT WINAPI class_factory_LockServer( IClassFactory *iface,
+                                                BOOL lock )
+{
+    FIXME( "%d: stub!\n", lock );
+    return S_OK;
+}
+
+static const struct IClassFactoryVtbl class_factory_vtbl =
+{
+    class_factory_QueryInterface,
+    class_factory_AddRef,
+    class_factory_Release,
+    class_factory_CreateInstance,
+    class_factory_LockServer
+};
+
+static struct class_factory thumb_cache_cf       = { { &class_factory_vtbl }, thumbcache_create };
+
+HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, LPVOID *obj)
+{
+    IClassFactory *cf = NULL;
+
+    TRACE( "(%s %s %p)\n", debugstr_guid( clsid ), debugstr_guid( iid ), obj );
+
+    if (IsEqualCLSID( clsid, &CLSID_LocalThumbnailCache ))
+        cf = &thumb_cache_cf.IClassFactory_iface;
+    else
+        FIXME( "(%s %s %p) unsupported interface\n", debugstr_guid( clsid ), debugstr_guid( iid ), obj );
+
+    if (!cf) return CLASS_E_CLASSNOTAVAILABLE;
+
+    return IClassFactory_QueryInterface( cf, iid, obj );
+}
diff --git a/dlls/thumbcache/thumbcache.spec b/dlls/thumbcache/thumbcache.spec
new file mode 100644
index 000000000000..b16365d0c9fc
--- /dev/null
+++ b/dlls/thumbcache/thumbcache.spec
@@ -0,0 +1,4 @@
+@ stdcall -private DllCanUnloadNow()
+@ stdcall -private DllGetClassObject(ptr ptr ptr)
+@ stdcall -private DllRegisterServer()
+@ stdcall -private DllUnregisterServer()
diff --git a/include/tpcshrd.h b/dlls/thumbcache/thumbcache_reg.idl
similarity index 58%
rename from include/tpcshrd.h
rename to dlls/thumbcache/thumbcache_reg.idl
index 8fe9d3fb8ac8..4b2ba62261a6 100644
--- a/include/tpcshrd.h
+++ b/dlls/thumbcache/thumbcache_reg.idl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 Alistair Leslie-Hughes
+ * Copyright 2026 Alistair Leslie-Hughes
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -15,17 +15,27 @@
  * 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 __WISPSHRD_H
-#define __WISPSHRD_H
 
-#define TABLET_DISABLE_PRESSANDHOLD        0x00000001
+#pragma makedep register
 
-#define WM_TABLET_DEFBASE                  0x02c0
-#define WM_TABLET_MAXOFFSET                0x20
-#define WM_TABLET_ADDED                    (WM_TABLET_DEFBASE + 8)
-#define WM_TABLET_DELETED                  (WM_TABLET_DEFBASE + 9)
-#define WM_TABLET_FLICK                    (WM_TABLET_DEFBASE + 11)
-#define WM_TABLET_QUERYSYSTEMGESTURESTATUS (WM_TABLET_DEFBASE + 12)
+import "unknwn.idl";
+import "objidl.idl";
+import "oaidl.idl";
 
+[
+    uuid(50ef4544-ac9f-4a8e-b21b-8a26180db13f),
+    threading(both)
+]
+coclass LocalThumbnailCache
+{
+    interface IThumbnailCache;
+}
 
-#endif /* __WISPSHRD_H */
+[
+    uuid(4db26476-6787-4046-b836-e8412a9e8a27),
+    threading(both)
+]
+coclass SharedBitmap
+{
+    interface ISharedBitmap;
+}
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c
index 2e5f0e6c6ea1..aa930e79fd6a 100644
--- a/dlls/uiautomationcore/uia_provider.c
+++ b/dlls/uiautomationcore/uia_provider.c
@@ -2317,3 +2317,10 @@ HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *elprov)
 
     return S_OK;
 }
+
+HRESULT WINAPI UiaDisconnectAllProviders(void)
+{
+    FIXME("stub\n");
+
+    return S_OK;
+}
diff --git a/dlls/uiautomationcore/uiautomationcore.spec b/dlls/uiautomationcore/uiautomationcore.spec
index 69e1f0ba9f99..ebde5ea0c65c 100644
--- a/dlls/uiautomationcore/uiautomationcore.spec
+++ b/dlls/uiautomationcore/uiautomationcore.spec
@@ -55,7 +55,7 @@
 @ stub TransformPattern_Rotate
 @ stdcall UiaAddEvent(ptr long ptr long ptr long ptr ptr)
 @ stdcall UiaClientsAreListening()
-#@ stub UiaDisconnectAllProviders
+@ stdcall UiaDisconnectAllProviders()
 @ stdcall UiaDisconnectProvider(ptr)
 @ stdcall UiaEventAddWindow(ptr long)
 @ stdcall UiaEventRemoveWindow(ptr long)
diff --git a/dlls/user32/button.c b/dlls/user32/button.c
index f4dbec7767c2..2c4bf1059635 100644
--- a/dlls/user32/button.c
+++ b/dlls/user32/button.c
@@ -48,8 +48,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(button);
 /* GetWindowLong offsets for window extra information */
 #define STATE_GWL_OFFSET  0
 #define HFONT_GWL_OFFSET  (sizeof(LONG))
-#define HIMAGE_GWL_OFFSET (HFONT_GWL_OFFSET+sizeof(HFONT))
-#define NB_EXTRA_BYTES    (HIMAGE_GWL_OFFSET+sizeof(HANDLE))
+#define HICON_GWL_OFFSET  (HFONT_GWL_OFFSET+sizeof(HFONT))
+#define NB_EXTRA_BYTES    (HICON_GWL_OFFSET+sizeof(HICON))
 
 /* undocumented flags */
 #define BUTTON_NSTATES         0x0F
@@ -110,27 +110,37 @@ static const pfPaint btnPaintFunc[MAX_BTN_TYPE] =
 };
 
 
-static inline LONG get_button_state( HWND hwnd )
+static LONG get_button_state( HWND hwnd )
 {
-    return GetWindowLongW( hwnd, STATE_GWL_OFFSET );
+    return NtUserGetPrivateData( hwnd, STATE_GWL_OFFSET, sizeof(LONG) );
 }
 
-static inline void set_button_state( HWND hwnd, LONG state )
+static LONG set_button_state( HWND hwnd, LONG state )
 {
-    SetWindowLongW( hwnd, STATE_GWL_OFFSET, state );
+    return NtUserSetPrivateData( hwnd, STATE_GWL_OFFSET, sizeof(LONG), state );
 }
 
-static inline HFONT get_button_font( HWND hwnd )
+static HFONT get_button_font( HWND hwnd )
 {
-    return (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET );
+    return (HFONT)NtUserGetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(HFONT) );
 }
 
-static inline void set_button_font( HWND hwnd, HFONT font )
+static HFONT set_button_font( HWND hwnd, HFONT font )
 {
-    SetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET, (LONG_PTR)font );
+    return (HFONT)NtUserSetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(HFONT), (LONG_PTR)font );
 }
 
-static inline UINT get_button_type( LONG window_style )
+static HANDLE get_button_icon( HWND hwnd )
+{
+    return (HANDLE)NtUserGetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(HICON) );
+}
+
+static HANDLE set_button_icon( HWND hwnd, HANDLE icon )
+{
+    return (HANDLE)NtUserSetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(HICON), (LONG_PTR)icon );
+}
+
+static UINT get_button_type( LONG window_style )
 {
     return (window_style & BS_TYPEMASK);
 }
@@ -168,6 +178,7 @@ LRESULT ButtonWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
     HANDLE oldHbitmap;
 
     if (!IsWindow( hWnd )) return 0;
+    if (uMsg == WM_NCCREATE) NtUserSetWindowFNID( hWnd, MAKE_FNID(NTUSER_WNDPROC_BUTTON) );
 
     pt.x = (short)LOWORD(lParam);
     pt.y = (short)HIWORD(lParam);
@@ -432,12 +443,12 @@ LRESULT ButtonWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
         default:
             return 0;
         }
-        oldHbitmap = (HBITMAP)SetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET, lParam );
+        oldHbitmap = set_button_icon( hWnd, (HANDLE)lParam );
 	NtUserInvalidateRect( hWnd, NULL, FALSE );
 	return (LRESULT)oldHbitmap;
 
     case BM_GETIMAGE:
-        return GetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET );
+        return (LRESULT)get_button_icon( hWnd );
 
     case BM_GETCHECK:
         return get_button_state( hWnd ) & 3;
@@ -576,7 +587,7 @@ static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
       }
 
       case BS_ICON:
-         if (!GetIconInfo((HICON)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo))
+         if (!GetIconInfo( get_button_icon( hwnd ), &iconInfo ))
             goto empty_rect;
 
          GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm);
@@ -589,7 +600,7 @@ static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
          break;
 
       case BS_BITMAP:
-         if (!GetObjectW( (HANDLE)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm))
+         if (!GetObjectW( get_button_icon( hwnd ), sizeof(BITMAP), &bm))
             goto empty_rect;
 
          r.right  = r.left + bm.bmWidth;
@@ -691,12 +702,12 @@ static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, const RECT *rc)
 
       case BS_ICON:
          flags |= DST_ICON;
-         lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
+         lp = (LPARAM)get_button_icon( hwnd );
          break;
 
       case BS_BITMAP:
          flags |= DST_BITMAP;
-         lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
+         lp = (LPARAM)get_button_icon( hwnd );
          break;
 
       default:
diff --git a/dlls/user32/class.c b/dlls/user32/class.c
index 125ab29c0a38..8c80126c90b3 100644
--- a/dlls/user32/class.c
+++ b/dlls/user32/class.c
@@ -124,67 +124,85 @@ void init_class_name( UNICODE_STRING *str, const WCHAR *name )
     }
 }
 
-static BOOL alloc_menu_nameA( struct client_menu_name *ret, const char *menu_name )
+static const WCHAR *menu_nameW( const struct client_menu_name *menu_name )
 {
-    if (!IS_INTRESOURCE(menu_name))
-    {
-        DWORD lenA = strlen( menu_name ) + 1;
-        DWORD lenW = MultiByteToWideChar( CP_ACP, 0, menu_name, lenA, NULL, 0 );
-        ret->nameW = HeapAlloc( GetProcessHeap(), 0, lenA + lenW * sizeof(WCHAR) );
-        if (!ret->nameW) return FALSE;
-        ret->nameA = (char *)(ret->nameW + lenW);
-        MultiByteToWideChar( CP_ACP, 0, menu_name, lenA, ret->nameW, lenW );
-        memcpy( ret->nameA, menu_name, lenA );
-    }
-    else
-    {
-        ret->nameW = (WCHAR *)menu_name;
-        ret->nameA = (char *)menu_name;
-    }
-    return TRUE;
+    if (IS_INTRESOURCE(menu_name)) return (const WCHAR *)menu_name;
+    return (const WCHAR *)(menu_name);
 }
 
-static BOOL alloc_menu_nameW( struct client_menu_name *ret, const WCHAR *menu_name )
+static const char *menu_nameA( const struct client_menu_name *menu_name )
 {
-    if (!IS_INTRESOURCE(menu_name))
-    {
-        DWORD lenW = lstrlenW( menu_name ) + 1;
-        DWORD lenA = WideCharToMultiByte( CP_ACP, 0, menu_name, lenW, NULL, 0, NULL, NULL );
-        ret->nameW = HeapAlloc( GetProcessHeap(), 0, lenA + lenW * sizeof(WCHAR) );
-        if (!ret->nameW) return FALSE;
-        ret->nameA = (char *)(ret->nameW + lenW);
-        memcpy( ret->nameW, menu_name, lenW * sizeof(WCHAR) );
-        WideCharToMultiByte( CP_ACP, 0, menu_name, lenW, ret->nameA, lenA, NULL, NULL );
-    }
-    else
-    {
-        ret->nameW = (WCHAR *)menu_name;
-        ret->nameA = (char *)menu_name;
-    }
-    return TRUE;
+    const WCHAR *nameW = menu_nameW( menu_name );
+    if (IS_INTRESOURCE(nameW)) return (const char *)nameW;
+    return (const char *)(nameW + wcslen( nameW ) + 1);
 }
 
-static void free_menu_name( struct client_menu_name *name )
+static struct client_menu_name *alloc_menu_nameA( const char *menu_name )
 {
-    if (!IS_INTRESOURCE(name->nameW)) HeapFree( GetProcessHeap(), 0, name->nameW );
+    UINT lenA, lenW;
+    WCHAR *nameW;
+
+    if (IS_INTRESOURCE(menu_name)) return (struct client_menu_name *)menu_name;
+
+    lenA = strlen( menu_name ) + 1;
+    lenW = MultiByteToWideChar( CP_ACP, 0, menu_name, lenA, NULL, 0 );
+    if (!(nameW = HeapAlloc( GetProcessHeap(), 0, lenA + lenW * sizeof(WCHAR) ))) return NULL;
+    MultiByteToWideChar( CP_ACP, 0, menu_name, lenA, nameW, lenW );
+    memcpy( nameW + lenW, menu_name, lenA );
+
+    return (struct client_menu_name *)nameW;
+}
+
+static struct client_menu_name *alloc_menu_nameW( const WCHAR *menu_name )
+{
+    UINT lenA, lenW;
+    WCHAR *nameW;
+
+    if (IS_INTRESOURCE(menu_name)) return (struct client_menu_name *)menu_name;
+
+    lenW = wcslen( menu_name ) + 1;
+    lenA = WideCharToMultiByte( CP_ACP, 0, menu_name, lenW, NULL, 0, NULL, NULL );
+    if (!(nameW = HeapAlloc( GetProcessHeap(), 0, lenA + lenW * sizeof(WCHAR) ))) return NULL;
+    memcpy( nameW, menu_name, lenW * sizeof(WCHAR) );
+    WideCharToMultiByte( CP_ACP, 0, menu_name, lenW, (char *)(nameW + lenW), lenA, NULL, NULL );
+
+    return (struct client_menu_name *)nameW;
 }
 
 static ULONG_PTR set_menu_nameW( HWND hwnd, INT offset, ULONG_PTR newval )
 {
-    struct client_menu_name menu_name;
-    if (!alloc_menu_nameW( &menu_name, (const WCHAR *)newval )) return 0;
-    NtUserSetClassLongPtr( hwnd, offset, (ULONG_PTR)&menu_name, FALSE );
-    free_menu_name( &menu_name );
-    return 0;
+    struct client_menu_name *menu_name = NULL;
+
+    if (newval && !(menu_name = alloc_menu_nameW( (const WCHAR *)newval ))) return 0;
+    menu_name = (struct client_menu_name *)NtUserSetClassLongPtr( hwnd, offset, (ULONG_PTR)menu_name, FALSE );
+    if (!IS_INTRESOURCE(menu_name)) free( menu_name );
+
+    return (ULONG_PTR)menu_name;
 }
 
 static ULONG_PTR set_menu_nameA( HWND hwnd, INT offset, ULONG_PTR newval )
 {
-    struct client_menu_name menu_name;
-    if (!alloc_menu_nameA( &menu_name, (const char *)newval )) return 0;
-    NtUserSetClassLongPtr( hwnd, offset, (ULONG_PTR)&menu_name, TRUE );
-    free_menu_name( &menu_name );
-    return 0;
+    struct client_menu_name *menu_name = NULL;
+
+    if (newval && !(menu_name = alloc_menu_nameA( (const char *)newval ))) return 0;
+    menu_name = (struct client_menu_name *)NtUserSetClassLongPtr( hwnd, offset, (ULONG_PTR)menu_name, FALSE );
+    if (!IS_INTRESOURCE(menu_name)) free( menu_name );
+
+    return (ULONG_PTR)menu_name;
+}
+
+static ULONG_PTR get_menu_nameW( HWND hwnd )
+{
+    const struct client_menu_name *menu_name;
+    if (!(menu_name = (void *)NtUserGetClassLongPtrW( hwnd, GCLP_MENUNAME ))) return 0;
+    return (ULONG_PTR)menu_nameW( menu_name );
+}
+
+static ULONG_PTR get_menu_nameA( HWND hwnd )
+{
+    const struct client_menu_name *menu_name;
+    if (!(menu_name = (void *)NtUserGetClassLongPtrW( hwnd, GCLP_MENUNAME ))) return 0;
+    return (ULONG_PTR)menu_nameA( menu_name );
 }
 
 void get_class_version( UNICODE_STRING *name, UNICODE_STRING *version, BOOL load )
@@ -329,7 +347,7 @@ ATOM WINAPI RegisterClassW( const WNDCLASSW* wc )
  */
 ATOM WINAPI RegisterClassExA( const WNDCLASSEXA* wc )
 {
-    struct client_menu_name menu_name;
+    struct client_menu_name *menu_name = NULL;
     WCHAR nameW[MAX_ATOM_LEN + 1];
     UNICODE_STRING name = RTL_CONSTANT_STRING(nameW), version;
     ATOM atom;
@@ -337,10 +355,10 @@ ATOM WINAPI RegisterClassExA( const WNDCLASSEXA* wc )
     init_class_name_ansi( &name, wc->lpszClassName );
     get_class_version( &name, &version, FALSE );
 
-    if (!alloc_menu_nameA( &menu_name, wc->lpszMenuName )) return 0;
+    if (wc->lpszMenuName && !(menu_name = alloc_menu_nameA( wc->lpszMenuName ))) return 0;
 
-    atom = NtUserRegisterClassExWOW( (WNDCLASSEXW *)wc, &name, &version, &menu_name, 0, 1, NULL );
-    if (!atom) free_menu_name( &menu_name );
+    atom = NtUserRegisterClassExWOW( (WNDCLASSEXW *)wc, &name, &version, menu_name, 0, 1, NULL );
+    if (!atom && !IS_INTRESOURCE(menu_name)) free( menu_name );
     return atom;
 }
 
@@ -350,7 +368,7 @@ ATOM WINAPI RegisterClassExA( const WNDCLASSEXA* wc )
  */
 ATOM WINAPI RegisterClassExW( const WNDCLASSEXW* wc )
 {
-    struct client_menu_name menu_name;
+    struct client_menu_name *menu_name = NULL;
     WCHAR nameW[MAX_ATOM_LEN + 1];
     UNICODE_STRING name = RTL_CONSTANT_STRING(nameW), version;
     ATOM atom;
@@ -358,10 +376,10 @@ ATOM WINAPI RegisterClassExW( const WNDCLASSEXW* wc )
     init_class_name( &name, wc->lpszClassName );
     get_class_version( &name, &version, FALSE );
 
-    if (!alloc_menu_nameW( &menu_name, wc->lpszMenuName )) return 0;
+    if (wc->lpszMenuName && !(menu_name = alloc_menu_nameW( wc->lpszMenuName ))) return 0;
 
-    atom = NtUserRegisterClassExWOW( wc, &name, &version, &menu_name, 0, 0, NULL );
-    if (!atom) free_menu_name( &menu_name );
+    atom = NtUserRegisterClassExWOW( wc, &name, &version, menu_name, 0, 0, NULL );
+    if (!atom && !IS_INTRESOURCE(menu_name)) free( menu_name );
     return atom;
 }
 
@@ -387,7 +405,7 @@ BOOL WINAPI UnregisterClassA( LPCSTR className, HINSTANCE hInstance )
  */
 BOOL WINAPI UnregisterClassW( LPCWSTR class_name, HINSTANCE instance )
 {
-    struct client_menu_name menu_name;
+    struct client_menu_name *menu_name;
     WCHAR nameW[MAX_ATOM_LEN + 1];
     UNICODE_STRING name = RTL_CONSTANT_STRING(nameW), version;
     BOOL ret;
@@ -396,7 +414,7 @@ BOOL WINAPI UnregisterClassW( LPCWSTR class_name, HINSTANCE instance )
     get_class_version( &name, &version, FALSE );
 
     ret = NtUserUnregisterClass( &name, instance, &menu_name );
-    if (ret) free_menu_name( &menu_name );
+    if (ret && !IS_INTRESOURCE(menu_name)) free( menu_name );
     return ret;
 }
 
@@ -406,6 +424,7 @@ BOOL WINAPI UnregisterClassW( LPCWSTR class_name, HINSTANCE instance )
  */
 WORD WINAPI GetClassWord( HWND hwnd, INT offset )
 {
+    if (offset == GCLP_MENUNAME) return get_menu_nameA( hwnd );
     return NtUserGetClassWord( hwnd, offset );
 }
 
@@ -415,6 +434,7 @@ WORD WINAPI GetClassWord( HWND hwnd, INT offset )
  */
 DWORD WINAPI GetClassLongW( HWND hwnd, INT offset )
 {
+    if (offset == GCLP_MENUNAME) return get_menu_nameW( hwnd );
     return NtUserGetClassLongW( hwnd, offset );
 }
 
@@ -425,6 +445,7 @@ DWORD WINAPI GetClassLongW( HWND hwnd, INT offset )
  */
 DWORD WINAPI GetClassLongA( HWND hwnd, INT offset )
 {
+    if (offset == GCLP_MENUNAME) return get_menu_nameA( hwnd );
     return NtUserGetClassLongA( hwnd, offset );
 }
 
@@ -480,7 +501,14 @@ INT WINAPI GetClassNameW( HWND hwnd, LPWSTR buffer, INT count )
  */
 UINT WINAPI RealGetWindowClassA( HWND hwnd, LPSTR buffer, UINT count )
 {
-    return GetClassNameA( hwnd, buffer, count );
+    WCHAR tmpbuf[MAX_ATOM_LEN + 1];
+    DWORD len;
+
+    if (count <= 0) return 0;
+    if (!RealGetWindowClassW( hwnd, tmpbuf, ARRAY_SIZE( tmpbuf ))) return 0;
+    RtlUnicodeToMultiByteN( buffer, count - 1, &len, tmpbuf, lstrlenW(tmpbuf) * sizeof(WCHAR) );
+    buffer[len] = 0;
+    return len;
 }
 
 
@@ -489,7 +517,8 @@ UINT WINAPI RealGetWindowClassA( HWND hwnd, LPSTR buffer, UINT count )
  */
 UINT WINAPI RealGetWindowClassW( HWND hwnd, LPWSTR buffer, UINT count )
 {
-    return GetClassNameW( hwnd, buffer, count );
+    UNICODE_STRING name = { .Buffer = buffer, .MaximumLength = count * sizeof(WCHAR) };
+    return NtUserGetClassName( hwnd, TRUE, &name );
 }
 
 
@@ -547,6 +576,7 @@ BOOL WINAPI GetClassInfoW( HINSTANCE hInstance, LPCWSTR name, WNDCLASSW *wc )
  */
 BOOL WINAPI GetClassInfoExA( HINSTANCE hInstance, LPCSTR name, WNDCLASSEXA *wc )
 {
+    struct client_menu_name *menu_name;
     WCHAR nameW[MAX_ATOM_LEN + 1];
     UNICODE_STRING name_str = RTL_CONSTANT_STRING(nameW), version;
     ATOM atom;
@@ -563,13 +593,14 @@ BOOL WINAPI GetClassInfoExA( HINSTANCE hInstance, LPCSTR name, WNDCLASSEXA *wc )
     init_class_name_ansi( &name_str, name );
     get_class_version( &name_str, &version, TRUE );
 
-    if (!(atom = NtUserGetClassInfoEx( hInstance, &name_str, (WNDCLASSEXW *)wc, NULL, TRUE )))
+    if (!(atom = NtUserGetClassInfoEx( hInstance, &name_str, (WNDCLASSEXW *)wc, &menu_name, TRUE )))
     {
         TRACE( "%s %p -> not found\n", debugstr_us(&name_str), hInstance );
         SetLastError( ERROR_CLASS_DOES_NOT_EXIST );
         return 0;
     }
 
+    wc->lpszMenuName = menu_nameA( menu_name );
     wc->lpszClassName = name;
     /* We must return the atom of the class here instead of just TRUE. */
     return atom;
@@ -581,6 +612,7 @@ BOOL WINAPI GetClassInfoExA( HINSTANCE hInstance, LPCSTR name, WNDCLASSEXA *wc )
  */
 BOOL WINAPI GetClassInfoExW( HINSTANCE hInstance, LPCWSTR name, WNDCLASSEXW *wc )
 {
+    struct client_menu_name *menu_name;
     WCHAR nameW[MAX_ATOM_LEN + 1];
     UNICODE_STRING name_str = RTL_CONSTANT_STRING(nameW), version;
     ATOM atom;
@@ -597,13 +629,14 @@ BOOL WINAPI GetClassInfoExW( HINSTANCE hInstance, LPCWSTR name, WNDCLASSEXW *wc
     init_class_name( &name_str, name );
     get_class_version( &name_str, &version, TRUE );
 
-    if (!(atom = NtUserGetClassInfoEx( hInstance, &name_str, wc, NULL, FALSE )))
+    if (!(atom = NtUserGetClassInfoEx( hInstance, &name_str, wc, &menu_name, FALSE )))
     {
         TRACE( "%s %p -> not found\n", debugstr_us(&name_str), hInstance );
         SetLastError( ERROR_CLASS_DOES_NOT_EXIST );
         return 0;
     }
 
+    wc->lpszMenuName = menu_nameW( menu_name );
     wc->lpszClassName = name;
     /* We must return the atom of the class here instead of just TRUE. */
     return atom;
@@ -624,6 +657,7 @@ BOOL WINAPI GetClassInfoExW( HINSTANCE hInstance, LPCWSTR name, WNDCLASSEXW *wc
  */
 ULONG_PTR WINAPI GetClassLongPtrA( HWND hwnd, INT offset )
 {
+    if (offset == GCLP_MENUNAME) return get_menu_nameA( hwnd );
     return NtUserGetClassLongPtrA( hwnd, offset );
 }
 
@@ -632,6 +666,7 @@ ULONG_PTR WINAPI GetClassLongPtrA( HWND hwnd, INT offset )
  */
 ULONG_PTR WINAPI GetClassLongPtrW( HWND hwnd, INT offset )
 {
+    if (offset == GCLP_MENUNAME) return get_menu_nameW( hwnd );
     return NtUserGetClassLongPtrW( hwnd, offset );
 }
 
diff --git a/dlls/user32/combo.c b/dlls/user32/combo.c
index 0d32c9a3f2d5..b251402602c9 100644
--- a/dlls/user32/combo.c
+++ b/dlls/user32/combo.c
@@ -69,6 +69,16 @@ static UINT	CBitHeight, CBitWidth;
 static void CBCalcPlacement(HEADCOMBO *combo);
 static void CBResetPos(HEADCOMBO *combo, BOOL redraw);
 
+static HEADCOMBO *get_control_state( HWND hwnd )
+{
+    return (HEADCOMBO *)NtUserGetPrivateData( hwnd, 0, sizeof(HEADCOMBO *) );
+}
+
+static HEADCOMBO *set_control_state( HWND hwnd, HEADCOMBO *state )
+{
+    return (HEADCOMBO *)NtUserSetPrivateData( hwnd, 0, sizeof(HEADCOMBO *), (LONG_PTR)state );
+}
+
 /***********************************************************************
  *           COMBO_Init
  *
@@ -116,7 +126,7 @@ static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
     if( COMBO_Init() && (lphc = calloc( 1, sizeof(HEADCOMBO) )) )
     {
         lphc->self = hwnd;
-        SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
+        set_control_state( hwnd, lphc );
 
        /* some braindead apps do try to use scrollbar/border flags */
 
@@ -154,7 +164,7 @@ static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
        if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
            NtUserDestroyWindow( lphc->hWndLBox );
 
-       SetWindowLongPtrW( lphc->self, 0, 0 );
+       set_control_state( lphc->self, NULL );
        free( lphc );
    }
    return 0;
@@ -1685,12 +1695,13 @@ static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
  */
 LRESULT ComboWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
 {
-      LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
+      LPHEADCOMBO lphc = get_control_state( hwnd );
 
       TRACE("[%p]: msg %s wp %08Ix lp %08Ix\n",
             hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
 
       if (!IsWindow(hwnd)) return 0;
+      if (message == WM_NCCREATE || message == WM_CREATE) NtUserSetWindowFNID( hwnd, MAKE_FNID(NTUSER_WNDPROC_COMBO) );
 
       if( lphc || message == WM_NCCREATE )
       switch(message)
diff --git a/dlls/user32/dialog.c b/dlls/user32/dialog.c
index 98365014d571..b3bad8482063 100644
--- a/dlls/user32/dialog.c
+++ b/dlls/user32/dialog.c
@@ -1157,8 +1157,8 @@ BOOL WINAPI IsDialogMessageW( HWND hwndDlg, LPMSG msg )
     if (NtUserCallMsgFilter( msg, MSGF_DIALOGBOX )) return TRUE;
 
     hwndDlg = WIN_GetFullHandle( hwndDlg );
-    if (is_desktop_window(hwndDlg)) return FALSE;
     if ((hwndDlg != msg->hwnd) && !IsChild( hwndDlg, msg->hwnd )) return FALSE;
+    if (!NtUserGetAncestor( hwndDlg, GA_PARENT )) return FALSE;
 
     hwndDlg = DIALOG_FindMsgDestination(hwndDlg);
 
diff --git a/dlls/user32/edit.c b/dlls/user32/edit.c
index cad148009953..eceefbdce8cc 100644
--- a/dlls/user32/edit.c
+++ b/dlls/user32/edit.c
@@ -147,6 +147,16 @@ static inline BOOL notify_parent(const EDITSTATE *es, INT code)
     return IsWindow(hwnd);
 }
 
+static EDITSTATE *get_control_state( HWND hwnd )
+{
+    return (EDITSTATE *)NtUserGetPrivateData( hwnd, 0, sizeof(EDITSTATE *) );
+}
+
+static EDITSTATE *set_control_state( HWND hwnd, EDITSTATE *state )
+{
+    return (EDITSTATE *)NtUserSetPrivateData( hwnd, 0, sizeof(EDITSTATE *), (LONG_PTR)state );
+}
+
 static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap);
 
 /*********************************************************************
@@ -4390,7 +4400,7 @@ static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTW lpcs, BOOL unicode)
 
 	if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
 		return FALSE;
-        SetWindowLongPtrW( hwnd, 0, (LONG_PTR)es );
+        set_control_state( hwnd, es );
 
        /*
         *      Note: since the EDITSTATE has not been fully initialized yet,
@@ -4479,7 +4489,7 @@ static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTW lpcs, BOOL unicode)
 	return TRUE;
 
 cleanup:
-	SetWindowLongPtrW(es->hwndSelf, 0, 0);
+	set_control_state( es->hwndSelf, NULL );
 	EDIT_InvalidateUniscribeData(es);
 	HeapFree(GetProcessHeap(), 0, es->first_line_def);
 	HeapFree(GetProcessHeap(), 0, es->undo_text);
@@ -4565,7 +4575,7 @@ static LRESULT EDIT_WM_NCDestroy(EDITSTATE *es)
 		pc = pp;
 	}
 
-	SetWindowLongPtrW( es->hwndSelf, 0, 0 );
+	set_control_state( es->hwndSelf, NULL );
 	HeapFree(GetProcessHeap(), 0, es->undo_text);
 	HeapFree(GetProcessHeap(), 0, es);
 
@@ -4590,12 +4600,14 @@ static inline LRESULT DefWindowProcT(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
  */
 LRESULT EditWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
 {
-	EDITSTATE *es = (EDITSTATE *)GetWindowLongPtrW( hwnd, 0 );
+	EDITSTATE *es = get_control_state( hwnd );
 	LRESULT result = 0;
 	POINT pt;
 
         TRACE("hwnd=%p msg=%x (%s) wparam=%Ix lparam=%Ix\n", hwnd, msg, SPY_GetMsgName(msg, hwnd), wParam, lParam);
 
+	if (msg == WM_NCCREATE) NtUserSetWindowFNID( hwnd, MAKE_FNID(NTUSER_WNDPROC_EDIT) );
+
 	if (!es && msg != WM_NCCREATE)
 		return DefWindowProcT(hwnd, msg, wParam, lParam, unicode);
 
diff --git a/dlls/user32/listbox.c b/dlls/user32/listbox.c
index 7989c2d350cb..c569b2968422 100644
--- a/dlls/user32/listbox.c
+++ b/dlls/user32/listbox.c
@@ -123,6 +123,16 @@ static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
 
 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
 
+static LB_DESCR *get_control_state( HWND hwnd )
+{
+    return (LB_DESCR *)NtUserGetPrivateData( hwnd, 0, sizeof(LB_DESCR *) );
+}
+
+static LB_DESCR *set_control_state( HWND hwnd, LB_DESCR *state )
+{
+    return (LB_DESCR *)NtUserSetPrivateData( hwnd, 0, sizeof(LB_DESCR *), (LONG_PTR)state );
+}
+
 /*
    For listboxes without LBS_NODATA, an array of LB_ITEMDATA is allocated
    to store the states of each item into descr->u.items.
@@ -2601,7 +2611,7 @@ static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
         descr->owner = lphc->self;
     }
 
-    SetWindowLongPtrW( descr->self, 0, (LONG_PTR)descr );
+    set_control_state( descr->self, descr );
 
 /*    if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
  */
@@ -2646,7 +2656,7 @@ static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
 {
     LISTBOX_ResetContent( descr );
-    SetWindowLongPtrW( descr->self, 0, 0 );
+    set_control_state( descr->self, NULL );
     HeapFree( GetProcessHeap(), 0, descr );
     return TRUE;
 }
@@ -2657,10 +2667,12 @@ static BOOL LISTBOX_Destroy( LB_DESCR *descr )
  */
 LRESULT ListBoxWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
 {
-    LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 );
+    LB_DESCR *descr = get_control_state( hwnd );
     HEADCOMBO *lphc = NULL;
     LRESULT ret;
 
+    if (msg == WM_CREATE || msg == WM_NCCREATE) NtUserSetWindowFNID( hwnd, MAKE_FNID(NTUSER_WNDPROC_LISTBOX) );
+
     if (!descr)
     {
         if (!IsWindow(hwnd)) return 0;
@@ -2670,7 +2682,7 @@ LRESULT ListBoxWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam
 	    CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
             if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams;
             if (!LISTBOX_Create( hwnd, lphc )) return -1;
-            TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) );
+            TRACE("creating hwnd %p descr %p\n", hwnd, get_control_state( hwnd ) );
             return 0;
         }
         /* Ignore all other messages before we get a WM_CREATE */
diff --git a/dlls/user32/message.c b/dlls/user32/message.c
index 53abd79f8ce7..42f4a80778c6 100644
--- a/dlls/user32/message.c
+++ b/dlls/user32/message.c
@@ -671,7 +671,7 @@ BOOL WINAPI InSendMessage(void)
  */
 DWORD WINAPI InSendMessageEx( LPVOID reserved )
 {
-    return NtUserGetThreadInfo()->receive_flags;
+    return NtUserGetThreadState( UserThreadStateInSendMessage );
 }
 
 
@@ -893,34 +893,6 @@ LRESULT WINAPI DECLSPEC_HOTPATCH DispatchMessageW( const MSG* msg )
 }
 
 
-/***********************************************************************
- *		GetMessagePos (USER.119)
- *		GetMessagePos (USER32.@)
- *
- * The GetMessagePos() function returns a long value representing a
- * cursor position, in screen coordinates, when the last message
- * retrieved by the GetMessage() function occurs. The x-coordinate is
- * in the low-order word of the return value, the y-coordinate is in
- * the high-order word. The application can use the MAKEPOINT()
- * macro to obtain a POINT structure from the return value.
- *
- * For the current cursor position, use GetCursorPos().
- *
- * RETURNS
- *
- * Cursor position of last message on success, zero on failure.
- *
- * CONFORMANCE
- *
- * ECMA-234, Win32
- *
- */
-DWORD WINAPI GetMessagePos(void)
-{
-    return NtUserGetThreadInfo()->message_pos;
-}
-
-
 /***********************************************************************
  *		GetMessageTime (USER.120)
  *		GetMessageTime (USER32.@)
@@ -952,18 +924,6 @@ LPARAM WINAPI GetMessageExtraInfo(void)
 }
 
 
-/***********************************************************************
- *		SetMessageExtraInfo (USER32.@)
- */
-LPARAM WINAPI SetMessageExtraInfo(LPARAM lParam)
-{
-    struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
-    LONG old_value = thread_info->message_extra;
-    thread_info->message_extra = lParam;
-    return old_value;
-}
-
-
 /***********************************************************************
  *		MsgWaitForMultipleObjects (USER32.@)
  */
diff --git a/dlls/user32/static.c b/dlls/user32/static.c
index 042255c0d889..aeb8ecf91f42 100644
--- a/dlls/user32/static.c
+++ b/dlls/user32/static.c
@@ -78,6 +78,25 @@ static const pfPaint staticPaintFunc[SS_TYPEMASK+1] =
     STATIC_PaintEtchedfn,    /* SS_ETCHEDFRAME */
 };
 
+static HFONT get_control_font( HWND hwnd )
+{
+    return (HFONT)NtUserGetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(HFONT) );
+}
+
+static HFONT set_control_font( HWND hwnd, HFONT font )
+{
+    return (HFONT)NtUserSetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(font), (LONG_PTR)font );
+}
+
+static HANDLE get_control_icon( HWND hwnd )
+{
+    return (HFONT)NtUserGetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(HICON) );
+}
+
+static HANDLE set_control_icon( HWND hwnd, HANDLE icon )
+{
+    return (HANDLE)NtUserSetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(icon), (LONG_PTR)icon );
+}
 
 /***********************************************************************
  *           STATIC_SetIcon
@@ -94,7 +113,7 @@ static HICON STATIC_SetIcon( HWND hwnd, HICON hicon, DWORD style )
         WARN("hicon != 0, but invalid\n");
         return 0;
     }
-    prevIcon = (HICON)NtUserSetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hicon), (LONG_PTR)hicon );
+    prevIcon = set_control_icon( hwnd, hicon );
     if (hicon && !(style & SS_CENTERIMAGE) && !(style & SS_REALSIZECONTROL))
     {
         /* Windows currently doesn't implement SS_RIGHTJUST */
@@ -127,7 +146,7 @@ static HBITMAP STATIC_SetBitmap( HWND hwnd, HBITMAP hBitmap, DWORD style )
         WARN("hBitmap != 0, but it's not a bitmap\n");
         return 0;
     }
-    hOldBitmap = (HBITMAP)NtUserSetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hBitmap), (LONG_PTR)hBitmap );
+    hOldBitmap = set_control_icon( hwnd, hBitmap );
     if (hBitmap && !(style & SS_CENTERIMAGE) && !(style & SS_REALSIZECONTROL))
     {
         BITMAP bm;
@@ -162,8 +181,7 @@ static HENHMETAFILE STATIC_SetEnhMetaFile( HWND hwnd, HENHMETAFILE hEnhMetaFile,
         WARN("hEnhMetaFile != 0, but it's not an enhanced metafile\n");
         return 0;
     }
-    return (HENHMETAFILE)NtUserSetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hEnhMetaFile),
-                                               (LONG_PTR)hEnhMetaFile );
+    return set_control_icon( hwnd, hEnhMetaFile );
 }
 
 /***********************************************************************
@@ -189,7 +207,7 @@ static HANDLE STATIC_GetImage( HWND hwnd, WPARAM wParam, DWORD style )
         default:
             return NULL;
     }
-    return (HANDLE)NtUserGetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(HANDLE) );
+    return get_control_icon( hwnd );
 }
 
 /***********************************************************************
@@ -325,6 +343,7 @@ LRESULT StaticWndProc_common( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam
     LONG style = full_style & SS_TYPEMASK;
 
     if (!IsWindow( hwnd )) return 0;
+    NtUserSetWindowFNID( hwnd, MAKE_FNID(NTUSER_WNDPROC_STATIC) );
 
     switch (uMsg)
     {
@@ -399,8 +418,6 @@ LRESULT StaticWndProc_common( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam
         {
             CREATESTRUCTW *cs = (CREATESTRUCTW *)lParam;
 
-            NtUserSetWindowFNID( hwnd, MAKE_FNID(0, HICON_GWL_OFFSET + sizeof(HANDLE)) );
-
             if (full_style & SS_SUNKEN || style == SS_ETCHEDHORZ || style == SS_ETCHEDVERT)
                 SetWindowLongW( hwnd, GWL_EXSTYLE,
                                 GetWindowLongW( hwnd, GWL_EXSTYLE ) | WS_EX_STATICEDGE );
@@ -487,7 +504,7 @@ LRESULT StaticWndProc_common( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam
     case WM_SETFONT:
         if (hasTextStyle( full_style ))
         {
-            NtUserSetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(HFONT), wParam );
+            set_control_font( hwnd, (HFONT)wParam );
             if (LOWORD(lParam))
                 NtUserRedrawWindow( hwnd, NULL, 0, RDW_INVALIDATE | RDW_ERASE |
                                     RDW_UPDATENOW | RDW_ALLCHILDREN );
@@ -495,7 +512,7 @@ LRESULT StaticWndProc_common( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam
         break;
 
     case WM_GETFONT:
-        return NtUserGetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(HFONT) );
+        return (LRESULT)get_control_font( hwnd );
 
     case WM_NCHITTEST:
         if (full_style & SS_NOTIFY)
@@ -577,7 +594,7 @@ static void STATIC_PaintOwnerDrawfn( HWND hwnd, HDC hdc, HBRUSH hbrush, DWORD st
   dis.itemData   = 0;
   GetClientRect( hwnd, &dis.rcItem );
 
-  font = (HFONT)NtUserGetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(HFONT) );
+  font = get_control_font( hwnd );
   if (font) oldFont = SelectObject( hdc, font );
   SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
   if (font) SelectObject( hdc, oldFont );
@@ -639,7 +656,7 @@ static void STATIC_PaintTextfn( HWND hwnd, HDC hdc, HBRUSH hbrush, DWORD style )
             format |= DT_SINGLELINE | DT_WORD_ELLIPSIS;
     }
 
-    if ((hFont = (HFONT)NtUserGetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(HFONT) )))
+    if ((hFont = get_control_font( hwnd )))
         hOldFont = SelectObject( hdc, hFont );
 
     if ((style & SS_TYPEMASK) != SS_SIMPLE)
@@ -728,7 +745,7 @@ static void STATIC_PaintIconfn( HWND hwnd, HDC hdc, HBRUSH hbrush, DWORD style )
     SIZE size;
 
     GetClientRect( hwnd, &rc );
-    hIcon = (HICON)NtUserGetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hIcon) );
+    hIcon = get_control_icon( hwnd );
     if (!hIcon || !get_icon_size( hIcon, &size ))
     {
         FillRect(hdc, &rc, hbrush);
@@ -755,7 +772,7 @@ static void STATIC_PaintBitmapfn(HWND hwnd, HDC hdc, HBRUSH hbrush, DWORD style
     HDC hMemDC;
     HBITMAP hBitmap, oldbitmap;
 
-    if ((hBitmap = (HBITMAP)NtUserGetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hBitmap) ))
+    if ((hBitmap = get_control_icon( hwnd ))
          && (GetObjectType(hBitmap) == OBJ_BITMAP)
          && (hMemDC = CreateCompatibleDC( hdc )))
     {
@@ -795,7 +812,7 @@ static void STATIC_PaintEnhMetafn(HWND hwnd, HDC hdc, HBRUSH hbrush, DWORD style
     
     GetClientRect(hwnd, &rc);
     FillRect(hdc, &rc, hbrush);
-    if ((hEnhMetaFile = (HENHMETAFILE)NtUserGetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hEnhMetaFile) )))
+    if ((hEnhMetaFile = get_control_icon( hwnd )))
     {
         /* The control's current font is not selected into the
            device context! */
diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c
index 6dc6c8871c28..c7a99cc80d43 100644
--- a/dlls/user32/tests/class.c
+++ b/dlls/user32/tests/class.c
@@ -50,6 +50,31 @@
 
 static const BOOL is_win64 = (sizeof(void *) > sizeof(int));
 
+#define run_in_process( a ) run_in_process_( __FILE__, __LINE__, a )
+static void run_in_process_( const char *file, int line, const char *args )
+{
+    char cmdline[MAX_PATH * 2], test[MAX_PATH], *tmp, **argv;
+    STARTUPINFOA startup = {.cb = sizeof(STARTUPINFOA)};
+    PROCESS_INFORMATION info = {0};
+    const char *name;
+    DWORD ret;
+    int argc;
+
+    name = file;
+    if ((tmp = strrchr( name, '\\' ))) name = tmp;
+    if ((tmp = strrchr( name, '/' ))) name = tmp;
+    strcpy( test, name );
+    if ((tmp = strrchr( test, '.' ))) *tmp = 0;
+
+    argc = winetest_get_mainargs( &argv );
+    sprintf( cmdline, "%s %s %s", argv[0], argc > 1 ? argv[1] : test, args );
+    ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info );
+    ok_(file, line)( ret, "CreateProcessA failed, error %lu\n", GetLastError() );
+    if (!ret) return;
+
+    wait_child_process( &info );
+}
+
 static const char comctl32_manifest[] =
 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
 "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n"
@@ -2374,7 +2399,6 @@ static void test_class_name(void)
     ok(!wcscmp(nameW, L"menu name"), "unexpected class name %s\n", debugstr_w(nameW));
 
     res = SetClassLongPtrA(hwnd, GCLP_MENUNAME, (LONG_PTR)"nameA");
-    todo_wine
     ok(res, "SetClassLongPtrA returned 0\n");
     nameA = (const char *)GetClassLongPtrA(hwnd, GCLP_MENUNAME);
     ok(!strcmp(nameA, "nameA"), "unexpected class name %s\n", debugstr_a(nameA));
@@ -2382,7 +2406,6 @@ static void test_class_name(void)
     ok(!wcscmp(nameW, L"nameA"), "unexpected class name %s\n", debugstr_w(nameW));
 
     res = SetClassLongPtrW(hwnd, GCLP_MENUNAME, (LONG_PTR)L"nameW");
-    todo_wine
     ok(res, "SetClassLongPtrW returned 0\n");
     nameA = (const char *)GetClassLongPtrA(hwnd, GCLP_MENUNAME);
     ok(!strcmp(nameA, "nameW"), "unexpected class name %s\n", debugstr_a(nameA));
@@ -2484,12 +2507,200 @@ void test_class_multithread(void)
     CloseHandle(thread_unregister);
 }
 
+struct real_class_test
+{
+    const char *class_name;
+    const char *real_class_name;
+    BOOL set_by_wm_null;
+    BOOL set_by_wm_create;
+    BOOL set_by_wm_nccreate;
+    BOOL test_cross_process;
+    BOOL wine_todo;
+};
+
+static const struct real_class_test class_tests[] =
+{
+    { "Button",            "Button",           FALSE, FALSE, TRUE,  TRUE,  FALSE },
+    { "ComboBox",          "ComboBox",         FALSE, TRUE,  TRUE,  TRUE,  FALSE },
+    { "Edit",              "Edit",             FALSE, FALSE, TRUE,  TRUE,  FALSE },
+    { "ListBox",           "ListBox",          FALSE, TRUE,  TRUE,  TRUE,  FALSE },
+    { "ScrollBar",         "ScrollBar",        FALSE, TRUE,  FALSE, TRUE,  TRUE },
+    { "Static",            "Static",           TRUE,  TRUE,  TRUE,  TRUE,  FALSE },
+    { "ComboLBox",         "ListBox",          FALSE, TRUE,  TRUE,  TRUE,  FALSE },
+    { "MDIClient",         "MDIClient",        TRUE,  TRUE,  TRUE,  TRUE,  TRUE },
+    { "#32768",            "#32768",           FALSE, FALSE, TRUE,  TRUE,  TRUE },
+    { "#32770",            "#32770",           TRUE,  TRUE,  TRUE,  TRUE,  TRUE },
+    /* Not all built-in classes set real window class. */
+    { "Message",           NULL,               FALSE, FALSE, FALSE, FALSE, FALSE },
+};
+
+static WNDPROC real_class_wndproc;
+static UINT real_class_message;
+static WNDCLASSA test_class;
+
+static LRESULT WINAPI test_real_class_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
+{
+    if (msg == real_class_message) CallWindowProcA( real_class_wndproc, hwnd, msg, wparam, lparam );
+    if (msg == WM_NCCREATE) return 1;
+    return 0;
+}
+
+#define check_real_class_name( a, b, c ) check_real_class_name_( __LINE__, a, b, c )
+static void check_real_class_name_( int line, HWND hwnd, const char *expect, BOOL todo )
+{
+    WCHAR expectW[256], nameW[256];
+    char nameA[256];
+    ULONG len;
+
+    len = RealGetWindowClassA( hwnd, nameA, ARRAY_SIZE(nameA) );
+    todo_wine_if( todo ) ok_(__FILE__, line)( !strcmp( nameA, expect ), "got %s\n", nameA );
+    todo_wine_if( todo ) ok_(__FILE__, line)( len == strlen( expect ), "got %ld\n", len );
+
+    MultiByteToWideChar( CP_ACP, 0, expect, -1, expectW, ARRAY_SIZE(expectW));
+    len = RealGetWindowClassW( hwnd, nameW, ARRAY_SIZE(nameW));
+    todo_wine_if( todo ) ok_(__FILE__, line)( !wcscmp( nameW, expectW ), "got %s\n", debugstr_w(nameW));
+    todo_wine_if( todo ) ok_(__FILE__, line)( len == wcslen( expectW ), "got %ld\n", len );
+}
+
+static void test_real_class_name_msg( UINT msg, const char *expect, BOOL cross_process, BOOL todo )
+{
+    const CLIENTCREATESTRUCT client_cs = {NULL, 1}; /* Needed for MDIClient. */
+    HWND hwnd;
+
+    real_class_message = msg;
+    hwnd = CreateWindowA( test_class.lpszClassName, "test", WS_OVERLAPPED, 0, 0, 50, 50, 0, 0, 0, (void *)&client_cs );
+    ok( !!hwnd, "got %p\n", hwnd );
+    if (msg == WM_NULL) SendMessageA( hwnd, WM_NULL, 0, 0 );
+
+    check_real_class_name( hwnd, expect ? expect : test_class.lpszClassName, expect ? todo : FALSE );
+    if (expect && cross_process)
+    {
+        char cmdline[MAX_PATH];
+        sprintf( cmdline, "test_RealGetWindowClass %p %s %d", hwnd, expect, todo );
+        run_in_process( cmdline );
+    }
+
+    DestroyWindow( hwnd );
+}
+
+static void test_RealGetWindowClass( void )
+{
+    WCHAR class_name_w[256];
+    char class_name[20];
+    HWND hwnd;
+    UINT ret;
+    int i;
+
+    hwnd = CreateWindowA( "Button", "test", BS_CHECKBOX | WS_POPUP, 0, 0, 50, 14, 0, 0, 0, NULL );
+    ok( !!hwnd, "hwnd == NULL\n" );
+
+    /* Basic tests. */
+    memset( class_name, 0, sizeof(class_name) );
+    ret = RealGetWindowClassA( hwnd, class_name, ARRAY_SIZE(class_name));
+    ok( !strcmp( class_name, "Button" ), "got %s\n", class_name );
+    ok( ret == strlen( class_name ), "got %d, %s\n", ret, class_name );
+
+    memset( class_name_w, 0, sizeof(class_name_w) );
+    ret = RealGetWindowClassW( hwnd, class_name_w, ARRAY_SIZE(class_name_w));
+    ok( !lstrcmpW( class_name_w, L"Button" ), "got %s\n", debugstr_w(class_name_w));
+    ok( ret == lstrlenW( class_name_w ), "got %d, %s\n", ret, debugstr_w(class_name_w));
+
+    /* Shortened buffer tests. */
+    memset( class_name, 0, sizeof(class_name) );
+    ret = RealGetWindowClassA( hwnd, class_name, 2 );
+    ok( !strcmp( class_name, "B" ), "got %s\n", class_name );
+    ok( ret == strlen( class_name ), "got %d\n", ret );
+
+    memset( class_name_w, 0, sizeof(class_name_w) );
+    ret = RealGetWindowClassW( hwnd, class_name_w, 2 );
+    ok( !lstrcmpW( class_name_w, L"B" ), "got %s\n", debugstr_w(class_name_w));
+    ok( ret == lstrlenW( class_name_w ), "got %d, %s\n", ret, debugstr_w(class_name_w));
+
+    /* A NULL buffer with a non-zero length will result in an access violation. */
+    if (0)
+    {
+        RealGetWindowClassA( hwnd, NULL, ARRAY_SIZE(class_name));
+    }
+
+    /* Invalid length. */
+    memset( class_name, 0, sizeof(class_name) );
+    SetLastError( 0xdeadbeef );
+    ret = RealGetWindowClassA( hwnd, class_name, 0 );
+    ok( !ret, "got %d\n", ret );
+    todo_wine
+    ok( (GetLastError() == ERROR_INSUFFICIENT_BUFFER), "Unexpected last error %ld\n", GetLastError() );
+
+    memset( class_name_w, 0, sizeof(class_name_w) );
+    SetLastError( 0xdeadbeef );
+    ret = RealGetWindowClassW( hwnd, class_name_w, 0 );
+    ok( !ret, "got %d\n", ret );
+    ok( (GetLastError() == ERROR_INSUFFICIENT_BUFFER), "Unexpected last error %ld\n", GetLastError() );
+
+    DestroyWindow( hwnd );
+
+    /* Custom class, RealGetWindowClass behaves the same as GetClassName. */
+    memset( &test_class, 0, sizeof(test_class) );
+    test_class.lpfnWndProc = ClassTest_WndProc2;
+    test_class.hInstance = GetModuleHandleA( NULL );
+    test_class.lpszClassName = "WineTest Class";
+    RegisterClassA( &test_class );
+
+    hwnd = CreateWindowA( test_class.lpszClassName, "test", WS_OVERLAPPED, 0, 0, 50, 50, 0, 0, 0, NULL );
+    ok( !!hwnd, "hwnd == NULL\n" );
+
+    check_real_class_name( hwnd, test_class.lpszClassName, FALSE );
+
+    DestroyWindow( hwnd );
+    UnregisterClassA( test_class.lpszClassName, GetModuleHandleA( NULL ) );
+
+    for (i = 0; i < ARRAY_SIZE(class_tests); i++)
+    {
+        const struct real_class_test *class_test = &class_tests[i];
+        BOOL test_cross_process = class_test->test_cross_process;
+
+        memset( &test_class, 0, sizeof(test_class) );
+        ret = GetClassInfoA( NULL, class_test->class_name, &test_class );
+        ok( ret, "GetClassInfoA failed: %lu\n", GetLastError() );
+        real_class_wndproc = test_class.lpfnWndProc;
+        test_class.lpfnWndProc = test_real_class_wndproc;
+        test_class.hInstance = GetModuleHandleA( NULL );
+        test_class.lpszClassName = "WineTest Class";
+        RegisterClassA( &test_class );
+
+        test_real_class_name_msg( WM_NULL, class_test->set_by_wm_null ? class_test->real_class_name : NULL,
+                                  test_cross_process, class_test->wine_todo );
+        if (class_test->set_by_wm_null) test_cross_process = FALSE;
+
+        test_real_class_name_msg( WM_NCCREATE, class_test->set_by_wm_nccreate ? class_test->real_class_name : NULL,
+                                  test_cross_process, class_test->wine_todo );
+        if (class_test->set_by_wm_nccreate) test_cross_process = FALSE;
+
+        /*
+         * If we pass WM_CREATE without WM_NCCREATE first to the Edit window
+         * procedure, it will trigger a divide by zero exception. Just skip this test.
+         */
+        if (!strcmp( class_test->class_name, "Edit" )) skip( "Skipping edit class\n");
+        else test_real_class_name_msg( WM_CREATE, class_test->set_by_wm_create ? class_test->real_class_name : NULL,
+                                       test_cross_process, class_test->wine_todo );
+        UnregisterClassA( test_class.lpszClassName, GetModuleHandleA( NULL ) );
+    }
+
+    real_class_wndproc = NULL;
+}
+
+static void test_RealGetWindowClass_process( HWND hwnd, const char *expect, BOOL todo )
+{
+    check_real_class_name( hwnd, expect, todo );
+}
+
 START_TEST(class)
 {
     char **argv;
     HANDLE hInstance = GetModuleHandleA( NULL );
     int argc = winetest_get_mainargs( &argv );
 
+    if (argc >= 3 && !strcmp( argv[2], "test_RealGetWindowClass" ))
+        return test_RealGetWindowClass_process( UlongToHandle( strtol( argv[3], NULL, 16 ) ), argv[4], strtol( argv[5], NULL, 0 ) );
     if (argc >= 3)
     {
         test_comctl32_class( argv[2] );
@@ -2521,6 +2732,7 @@ START_TEST(class)
     test_actctx_classes();
     test_class_name();
     test_class_multithread();
+    test_RealGetWindowClass();
 
     /* this test unregisters the Button class so it should be executed at the end */
     test_instances();
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c
index f41f54b48294..6ad01e829783 100644
--- a/dlls/user32/tests/input.c
+++ b/dlls/user32/tests/input.c
@@ -5147,6 +5147,13 @@ static LRESULT WINAPI msg_source_proc( HWND hwnd, UINT message, WPARAM wp, LPARA
         SendMessageA( hwnd, WM_USER, 0, 0 );
         PostMessageA( hwnd, WM_USER, 0, 0 );
         if (PeekMessageW( &msg, hwnd, WM_USER, WM_USER, PM_REMOVE )) DispatchMessageW( &msg );
+        ok( source.deviceType == expect_src.deviceType || /* also accept system-generated WM_MOUSEMOVE */
+            (message == WM_MOUSEMOVE && source.deviceType == IMDT_UNAVAILABLE),
+            "%x: wrong deviceType %x/%x\n", message, source.deviceType, expect_src.deviceType );
+        ok( source.originId == expect_src.originId ||
+            (message == WM_MOUSEMOVE && source.originId == IMO_SYSTEM),
+            "%x: wrong originId %x/%x\n", message, source.originId, expect_src.originId );
+        ok( !PeekMessageW( &msg, hwnd, WM_USER, WM_USER, PM_REMOVE ), "got pending WM_USER\n" );
         ok( source.deviceType == expect_src.deviceType || /* also accept system-generated WM_MOUSEMOVE */
             (message == WM_MOUSEMOVE && source.deviceType == IMDT_UNAVAILABLE),
             "%x: wrong deviceType %x/%x\n", message, source.deviceType, expect_src.deviceType );
@@ -5157,20 +5164,76 @@ static LRESULT WINAPI msg_source_proc( HWND hwnd, UINT message, WPARAM wp, LPARA
     default:
         ok( source.deviceType == IMDT_UNAVAILABLE, "%x: wrong deviceType %x\n",
             message, source.deviceType );
-        ok( source.originId == 0, "%x: wrong originId %x\n", message, source.originId );
+        ok( source.originId == IMO_UNAVAILABLE, "%x: wrong originId %x\n", message, source.originId );
         break;
     }
 
     return DefWindowProcA( hwnd, message, wp, lp );
 }
 
+static LRESULT WINAPI get_message_hook( int code, WPARAM wp, LPARAM lp )
+{
+    INPUT_MESSAGE_SOURCE source;
+    MSG *msg = (MSG *)lp;
+    UINT message = msg->message;
+
+    if (code < 0) return CallNextHookEx( 0, code, wp, lp );
+
+    ok( pGetCurrentInputMessageSource( &source ), "GetCurrentInputMessageSource failed\n" );
+    switch (message)
+    {
+    case WM_KEYDOWN:
+    case WM_KEYUP:
+    case WM_SYSKEYDOWN:
+    case WM_SYSKEYUP:
+    case WM_MOUSEMOVE:
+    case WM_LBUTTONDOWN:
+    case WM_LBUTTONUP:
+    case WM_RBUTTONDOWN:
+    case WM_RBUTTONUP:
+        ok( source.deviceType == expect_src.deviceType || /* also accept system-generated WM_MOUSEMOVE */
+            (message == WM_MOUSEMOVE && source.deviceType == IMDT_UNAVAILABLE),
+            "%x: wrong deviceType %x/%x\n", message, source.deviceType, expect_src.deviceType );
+        ok( source.originId == expect_src.originId ||
+            (message == WM_MOUSEMOVE && source.originId == IMO_SYSTEM),
+            "%x: wrong originId %x/%x\n", message, source.originId, expect_src.originId );
+        break;
+    default:
+        ok( source.deviceType == IMDT_UNAVAILABLE, "%x: wrong deviceType %x\n",
+            message, source.deviceType );
+        ok( source.originId == IMO_UNAVAILABLE, "%x: wrong originId %x\n",
+            message, source.originId );
+        break;
+    }
+    return CallNextHookEx( 0, code, wp, lp );
+}
+
+static LRESULT WINAPI keyboard_hook( int code, WPARAM wp, LPARAM lp )
+{
+    INPUT_MESSAGE_SOURCE source;
+
+    if (code < 0) return CallNextHookEx( 0, code, wp, lp );
+
+    ok( pGetCurrentInputMessageSource( &source ), "GetCurrentInputMessageSource failed\n" );
+    ok( source.deviceType == expect_src.deviceType ||
+        broken( source.deviceType == IMDT_UNAVAILABLE ), /* <= win10 1507 */
+        "wrong deviceType %x/%x\n", source.deviceType, expect_src.deviceType );
+    ok( source.originId == expect_src.originId ||
+        broken( source.originId == IMO_UNAVAILABLE), /* <= win10 1507 */
+        "wrong originId %x/%x\n", source.originId, expect_src.originId );
+
+    return CallNextHookEx( 0, code, wp, lp );
+}
+
 static void test_input_message_source(void)
 {
+    INPUT_MESSAGE_SOURCE source;
     WNDCLASSA cls;
     INPUT inputs[2];
     HWND hwnd;
     RECT rc;
     MSG msg;
+    HHOOK msg_hook, kbd_hook;
 
     cls.style = 0;
     cls.lpfnWndProc = msg_source_proc;
@@ -5190,6 +5253,11 @@ static void test_input_message_source(void)
     SetForegroundWindow( hwnd );
     SetFocus( hwnd );
 
+    msg_hook = SetWindowsHookExW( WH_GETMESSAGE, get_message_hook, NULL, GetCurrentThreadId() );
+    ok( msg_hook != NULL, "SetWindowsHookEx failed\n" );
+    kbd_hook = SetWindowsHookExW( WH_KEYBOARD, keyboard_hook, NULL, GetCurrentThreadId() );
+    ok( kbd_hook != NULL, "SetWindowsHookEx failed\n" );
+
     inputs[0].type = INPUT_KEYBOARD;
     inputs[0].ki.dwExtraInfo = 0;
     inputs[0].ki.time = 0;
@@ -5204,25 +5272,29 @@ static void test_input_message_source(void)
     SendMessageA( hwnd, WM_KEYDOWN, 0, 0 );
     SendMessageA( hwnd, WM_MOUSEMOVE, 0, 0 );
 
+    expect_src.deviceType = IMDT_KEYBOARD;
+    expect_src.originId = IMO_INJECTED;
     SendInput( 2, inputs, sizeof(INPUT) );
     while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE ))
     {
-        expect_src.deviceType = IMDT_KEYBOARD;
-        expect_src.originId = IMO_INJECTED;
         TranslateMessage( &msg );
         DispatchMessageW( &msg );
     }
     GetWindowRect( hwnd, &rc );
     simulate_click( TRUE, (rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2 );
     simulate_click( FALSE, (rc.left + rc.right) / 2 + 1, (rc.top + rc.bottom) / 2 + 1 );
+    expect_src.deviceType = IMDT_MOUSE;
+    expect_src.originId = IMO_INJECTED;
     while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE ))
     {
-        expect_src.deviceType = IMDT_MOUSE;
-        expect_src.originId = IMO_INJECTED;
         TranslateMessage( &msg );
         DispatchMessageW( &msg );
     }
 
+    ok( pGetCurrentInputMessageSource( &source ), "GetCurrentInputMessageSource failed\n" );
+    ok( source.deviceType == IMDT_UNAVAILABLE, "wrong type %x\n", source.deviceType );
+    ok( source.originId == IMO_UNAVAILABLE, "wrong origin %x\n", source.originId );
+
     expect_src.deviceType = IMDT_UNAVAILABLE;
     expect_src.originId = IMO_UNAVAILABLE;
     SendMessageA( hwnd, WM_KEYDOWN, 0, 0 );
@@ -5235,6 +5307,10 @@ static void test_input_message_source(void)
         DispatchMessageW( &msg );
     }
 
+    ok( pGetCurrentInputMessageSource( &source ), "GetCurrentInputMessageSource failed\n" );
+    ok( source.deviceType == IMDT_UNAVAILABLE, "wrong type %x\n", source.deviceType );
+    ok( source.originId == IMO_UNAVAILABLE, "wrong origin %x\n", source.originId );
+
     expect_src.deviceType = IMDT_UNAVAILABLE;
     expect_src.originId = IMO_SYSTEM;
     SetCursorPos( (rc.left + rc.right) / 2 - 1, (rc.top + rc.bottom) / 2 - 1 );
@@ -5244,8 +5320,14 @@ static void test_input_message_source(void)
         DispatchMessageW( &msg );
     }
 
+    ok( pGetCurrentInputMessageSource( &source ), "GetCurrentInputMessageSource failed\n" );
+    ok( source.deviceType == IMDT_UNAVAILABLE, "wrong type %x\n", source.deviceType );
+    ok( source.originId == IMO_UNAVAILABLE, "wrong origin %x\n", source.originId );
+
     DestroyWindow( hwnd );
     UnregisterClassA( cls.lpszClassName, GetModuleHandleA(0) );
+    UnhookWindowsHookEx( msg_hook );
+    UnhookWindowsHookEx( kbd_hook );
 }
 
 static void test_UnregisterDeviceNotification(void)
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec
index 40e80c692469..fe41f9cb2e70 100644
--- a/dlls/user32/user32.spec
+++ b/dlls/user32/user32.spec
@@ -613,7 +613,7 @@
 @ stdcall GetMenuStringW(long long ptr long long)
 @ stdcall GetMessageA(ptr long long long)
 @ stdcall GetMessageExtraInfo()
-@ stdcall GetMessagePos()
+@ stdcall GetMessagePos() NtUserGetMessagePos
 @ stdcall GetMessageTime()
 @ stdcall GetMessageW(ptr long long long)
 @ stdcall GetMonitorInfoA(long ptr)
@@ -1064,7 +1064,7 @@
 @ stdcall SetMenuItemBitmaps(long long long long long)
 @ stdcall SetMenuItemInfoA(long long long ptr)
 @ stdcall SetMenuItemInfoW(long long long ptr)
-@ stdcall SetMessageExtraInfo(long)
+@ stdcall SetMessageExtraInfo(long) NtUserSetMessageExtraInfo
 @ stdcall SetMessageQueue(long)
 # @ stub SetMirrorRendering
 @ stdcall SetParent(long long) NtUserSetParent
diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h
index 3513e5022957..abb84b0e8802 100644
--- a/dlls/user32/user_private.h
+++ b/dlls/user32/user_private.h
@@ -171,7 +171,6 @@ void WINAPI USER_ScrollBarDraw(HWND, HDC, INT, enum SCROLL_HITTEST,
 struct scroll_info *SCROLL_GetInternalInfo( HWND hwnd, INT nBar, BOOL alloc );
 
 /* Window functions */
-BOOL is_desktop_window( HWND hwnd );
 HWND WIN_GetFullHandle( HWND hwnd );
 HWND WIN_IsCurrentProcess( HWND hwnd );
 HWND WIN_IsCurrentThread( HWND hwnd );
diff --git a/dlls/user32/win.c b/dlls/user32/win.c
index ac8b33a9c840..bcf88ee21bd2 100644
--- a/dlls/user32/win.c
+++ b/dlls/user32/win.c
@@ -94,28 +94,6 @@ static BOOL enum_windows( HDESK desktop, HWND hwnd, DWORD tid, BOOL children,
 }
 
 
-/*******************************************************************
- *           is_desktop_window
- *
- * Check if window is the desktop or the HWND_MESSAGE top parent.
- */
-BOOL is_desktop_window( HWND hwnd )
-{
-    struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
-
-    if (!hwnd) return FALSE;
-    if (hwnd == UlongToHandle( thread_info->top_window )) return TRUE;
-    if (hwnd == UlongToHandle( thread_info->msg_window )) return TRUE;
-
-    if (!HIWORD(hwnd) || HIWORD(hwnd) == 0xffff)
-    {
-        if (LOWORD(thread_info->top_window) == LOWORD(hwnd)) return TRUE;
-        if (LOWORD(thread_info->msg_window) == LOWORD(hwnd)) return TRUE;
-    }
-    return FALSE;
-}
-
-
 /* check if hwnd is a broadcast magic handle */
 static inline BOOL is_broadcast( HWND hwnd )
 {
@@ -283,6 +261,7 @@ HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module,
 {
     WCHAR nameW[MAX_ATOM_LEN + 1];
     UNICODE_STRING class = RTL_CONSTANT_STRING(nameW), version, window_name = {0};
+    struct client_menu_name *menu_name;
     HWND hwnd, top_child = 0;
     MDICREATESTRUCTW mdi_cs;
     WNDCLASSEXW info;
@@ -292,7 +271,7 @@ HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module,
     init_class_name( &class, className );
     get_class_version( &class, &version, TRUE );
 
-    if (!NtUserGetClassInfoEx( module, &class, &info, NULL, FALSE ))
+    if (!NtUserGetClassInfoEx( module, &class, &info, &menu_name, FALSE ))
     {
         TRACE( "%s %p -> not found\n", debugstr_us(&class), module );
         SetLastError( ERROR_CLASS_DOES_NOT_EXIST );
@@ -572,9 +551,6 @@ HWND WINAPI FindWindowW( LPCWSTR className, LPCWSTR title )
  */
 HWND WINAPI GetDesktopWindow(void)
 {
-    struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
-
-    if (thread_info->top_window) return UlongToHandle( thread_info->top_window );
     return NtUserGetDesktopWindow();
 }
 
diff --git a/dlls/uxtheme/draw.c b/dlls/uxtheme/draw.c
index 91406407ea3a..2a1f023e38c6 100644
--- a/dlls/uxtheme/draw.c
+++ b/dlls/uxtheme/draw.c
@@ -104,11 +104,11 @@ HRESULT WINAPI DrawThemeParentBackground(HWND hwnd, HDC hdc, RECT *prc)
     HWND hParent;
     HRGN clip = NULL;
     int hasClip = -1;
-    
+
     TRACE("(%p,%p,%p)\n", hwnd, hdc, prc);
     hParent = GetParent(hwnd);
     if(!hParent)
-        hParent = hwnd;
+        return S_OK;
     if(prc) {
         rt = *prc;
         MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
diff --git a/dlls/uxtheme/tests/system.c b/dlls/uxtheme/tests/system.c
index d5eac85646cd..6c7993e5757f 100644
--- a/dlls/uxtheme/tests/system.c
+++ b/dlls/uxtheme/tests/system.c
@@ -1741,6 +1741,14 @@ static void test_DrawThemeParentBackground(void)
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
     ReleaseDC(child, hdc);
+
+    hdc = GetDC(parent);
+    hr = DrawThemeParentBackground(parent, hdc, NULL);
+    ok(SUCCEEDED(hr), "DrawThemeParentBackground failed, hr %#lx.\n", hr);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, EmptySeq, "DrawThemeParentBackground", FALSE);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    ReleaseDC(parent, hdc);
+
     DestroyWindow(parent);
     UnregisterClassA("TestDrawThemeParentBackgroundClass", GetModuleHandleA(0));
 }
diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c
index 940703762f50..4a52079e1f0f 100644
--- a/dlls/vbscript/compile.c
+++ b/dlls/vbscript/compile.c
@@ -1398,7 +1398,7 @@ static HRESULT compile_const_statement(compile_ctx_t *ctx, const_statement_t *st
             }
         }
 
-        if(lookup_func_decls(ctx, decl->name)) {
+        if(ctx->func->type == FUNC_GLOBAL && lookup_func_decls(ctx, decl->name)) {
             ctx->loc = decl->loc;
             WARN("%s redefined\n", debugstr_w(decl->name));
             return MAKE_VBSERROR(VBSE_NAME_REDEFINED);
@@ -2073,6 +2073,11 @@ static HRESULT compile_class(compile_ctx_t *ctx, class_decl_t *class_decl)
         return E_OUTOFMEMORY;
     memset(class_desc->funcs, 0, class_desc->func_cnt*sizeof(*class_desc->funcs));
 
+    /* Class members have their own namespace: drop the global Dim/Const scope
+       before compiling the methods. Same-class collisions are caught below. */
+    ctx->dim_decls = ctx->dim_decls_tail = NULL;
+    ctx->const_decls = NULL;
+
     for(func_decl = class_decl->funcs, i=1; func_decl; func_decl = func_decl->next, i++) {
         for(func_prop_decl = func_decl; func_prop_decl; func_prop_decl = func_prop_decl->next_prop_func) {
             if(func_prop_decl->is_default) {
@@ -2355,6 +2360,7 @@ HRESULT compile_script(script_ctx_t *script, const WCHAR *src, const WCHAR *item
     function_decl_t *func_decl;
     named_item_t *item = NULL;
     class_decl_t *class_decl;
+    dim_decl_t *global_dims;
     function_t *new_func;
     compile_ctx_t ctx;
     vbscode_t *code;
@@ -2399,8 +2405,15 @@ HRESULT compile_script(script_ctx_t *script, const WCHAR *src, const WCHAR *item
     ctx.global_consts = ctx.const_decls;
     code->option_explicit = ctx.parser.option_explicit;
 
+    /* compile_func() repurposes ctx.dim_decls and ctx.const_decls for each
+       function's locals, so capture the global dims now and restore the global
+       scope before each top-level redefinition check: a function or class name
+       collides only with a global Dim/Const, not another function's local one. */
+    global_dims = ctx.dim_decls;
 
     for(func_decl = ctx.func_decls; func_decl; func_decl = func_decl->next) {
+        ctx.dim_decls = global_dims;
+        ctx.const_decls = ctx.global_consts;
         hres = create_function(&ctx, func_decl, &new_func);
         if(FAILED(hres)) {
             hres = compile_error(script, &ctx, hres);
@@ -2413,6 +2426,8 @@ HRESULT compile_script(script_ctx_t *script, const WCHAR *src, const WCHAR *item
     }
 
     for(class_decl = ctx.parser.class_decls; class_decl; class_decl = class_decl->next) {
+        ctx.dim_decls = global_dims;
+        ctx.const_decls = ctx.global_consts;
         hres = compile_class(&ctx, class_decl);
         if(FAILED(hres)) {
             hres = compile_error(script, &ctx, hres);
diff --git a/dlls/vbscript/global.c b/dlls/vbscript/global.c
index bd949f7b370e..5cf167fc4f1c 100644
--- a/dlls/vbscript/global.c
+++ b/dlls/vbscript/global.c
@@ -647,6 +647,17 @@ static HRESULT show_msgbox(script_ctx_t *ctx, BSTR prompt, unsigned type, BSTR o
     return return_short(res, ret);
 }
 
+/* Cxxx() coercion functions raise err 94 (Illegal Null use) on Null and
+ * err 91 (Object variable not set) on Nothing, matching native VBScript. */
+static HRESULT check_coerce_arg(const VARIANT *arg)
+{
+    if(V_VT(arg) == VT_NULL)
+        return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE);
+    if(V_VT(arg) == VT_DISPATCH && !V_DISPATCH(arg))
+        return MAKE_VBSERROR(VBSE_OBJECT_VARIABLE_NOT_SET);
+    return S_OK;
+}
+
 static HRESULT Global_CCur(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res)
 {
     VARIANT v;
@@ -656,6 +667,10 @@ static HRESULT Global_CCur(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V
 
     assert(args_cnt == 1);
 
+    hres = check_coerce_arg(arg);
+    if(FAILED(hres))
+        return hres;
+
     V_VT(&v) = VT_EMPTY;
     hres = VariantChangeType(&v, arg, 0, VT_CY);
     if(FAILED(hres))
@@ -679,6 +694,10 @@ static HRESULT Global_CInt(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V
 
     assert(args_cnt == 1);
 
+    hres = check_coerce_arg(arg);
+    if(FAILED(hres))
+        return hres;
+
     V_VT(&v) = VT_EMPTY;
     hres = VariantChangeType(&v, arg, 0, VT_I2);
     if(FAILED(hres))
@@ -701,6 +720,10 @@ static HRESULT Global_CLng(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V
 
     assert(args_cnt == 1);
 
+    hres = check_coerce_arg(arg);
+    if(FAILED(hres))
+        return hres;
+
     hres = to_int(arg, &i);
     if(FAILED(hres))
         return hres;
@@ -719,6 +742,10 @@ static HRESULT Global_CBool(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt,
 
     assert(args_cnt == 1);
 
+    hres = check_coerce_arg(arg);
+    if(FAILED(hres))
+        return hres;
+
     V_VT(&v) = VT_EMPTY;
     hres = VariantChangeType(&v, arg, VARIANT_LOCALBOOL, VT_BOOL);
     if(FAILED(hres))
@@ -740,6 +767,10 @@ static HRESULT Global_CByte(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt,
 
     assert(args_cnt == 1);
 
+    hres = check_coerce_arg(arg);
+    if(FAILED(hres))
+        return hres;
+
     V_VT(&v) = VT_EMPTY;
     hres = VariantChangeType(&v, arg, VARIANT_LOCALBOOL, VT_UI1);
     if(FAILED(hres))
@@ -763,8 +794,9 @@ static HRESULT Global_CDate(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt,
 
     assert(args_cnt == 1);
 
-    if(V_VT(arg) == VT_NULL)
-        return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE);
+    hres = check_coerce_arg(arg);
+    if(FAILED(hres))
+        return hres;
 
     V_VT(&v) = VT_EMPTY;
     hres = VariantChangeType(&v, arg, 0, VT_DATE);
@@ -792,6 +824,10 @@ static HRESULT Global_CDbl(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V
 
     assert(args_cnt == 1);
 
+    hres = check_coerce_arg(arg);
+    if(FAILED(hres))
+        return hres;
+
     V_VT(&v) = VT_EMPTY;
     hres = VariantChangeType(&v, arg, 0, VT_R8);
     if(FAILED(hres))
@@ -814,6 +850,10 @@ static HRESULT Global_CSng(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V
 
     assert(args_cnt == 1);
 
+    hres = check_coerce_arg(arg);
+    if(FAILED(hres))
+        return hres;
+
     V_VT(&v) = VT_EMPTY;
     hres = VariantChangeType(&v, arg, 0, VT_R4);
     if(FAILED(hres))
@@ -833,8 +873,9 @@ static HRESULT Global_CStr(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V
 
     TRACE("%s\n", debugstr_variant(arg));
 
-    if(V_VT(arg) == VT_NULL)
-        return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE);
+    hres = check_coerce_arg(arg);
+    if(FAILED(hres))
+        return hres;
 
     hres = to_string(This->ctx->lcid, arg, &str);
     if(FAILED(hres))
@@ -1168,6 +1209,8 @@ static HRESULT Global_Timer(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt,
 
 static HRESULT Global_LBound(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res)
 {
+    VARIANT default_value;
+    VARIANT *array_arg = arg;
     SAFEARRAY *sa;
     HRESULT hres;
     LONG lbound;
@@ -1177,41 +1220,56 @@ static HRESULT Global_LBound(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt,
 
     TRACE("%s %s\n", debugstr_variant(arg), args_cnt == 2 ? debugstr_variant(arg + 1) : "1");
 
-    switch(V_VT(arg)) {
+    V_VT(&default_value) = VT_EMPTY;
+
+    if(V_VT(arg) == VT_DISPATCH) {
+        DISPPARAMS dp = {0};
+        if(!V_DISPATCH(arg))
+            return MAKE_VBSERROR(VBSE_OBJECT_VARIABLE_NOT_SET);
+        hres = disp_call(This->ctx, V_DISPATCH(arg), DISPID_VALUE, TRUE, &dp, &default_value);
+        if(FAILED(hres))
+            return hres;
+        array_arg = &default_value;
+    }
+
+    switch(V_VT(array_arg)) {
     case VT_VARIANT|VT_ARRAY:
-        sa = V_ARRAY(arg);
+        sa = V_ARRAY(array_arg);
         break;
     case VT_VARIANT|VT_ARRAY|VT_BYREF:
-        sa = *V_ARRAYREF(arg);
+        sa = *V_ARRAYREF(array_arg);
         break;
-    case VT_EMPTY:
-    case VT_NULL:
-        return MAKE_VBSERROR(VBSE_TYPE_MISMATCH);
     default:
-        FIXME("arg %s not supported\n", debugstr_variant(arg));
-        return E_NOTIMPL;
+        VariantClear(&default_value);
+        return MAKE_VBSERROR(VBSE_TYPE_MISMATCH);
     }
 
-    if(!sa)
-        return MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS);
+    if(!sa) {
+        hres = MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS);
+        goto done;
+    }
 
     if(args_cnt == 2) {
         hres = to_int(arg + 1, &dim);
         if(FAILED(hres))
-            return hres;
+            goto done;
     }else {
         dim = 1;
     }
 
     hres = SafeArrayGetLBound(sa, dim, &lbound);
-    if(FAILED(hres))
-        return hres;
+    if(SUCCEEDED(hres))
+        hres = return_int(res, lbound);
 
-    return return_int(res, lbound);
+done:
+    VariantClear(&default_value);
+    return hres;
 }
 
 static HRESULT Global_UBound(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res)
 {
+    VARIANT default_value;
+    VARIANT *array_arg = arg;
     SAFEARRAY *sa;
     HRESULT hres;
     LONG ubound;
@@ -1221,37 +1279,50 @@ static HRESULT Global_UBound(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt,
 
     TRACE("%s %s\n", debugstr_variant(arg), args_cnt == 2 ? debugstr_variant(arg + 1) : "1");
 
-    switch(V_VT(arg)) {
+    V_VT(&default_value) = VT_EMPTY;
+
+    if(V_VT(arg) == VT_DISPATCH) {
+        DISPPARAMS dp = {0};
+        if(!V_DISPATCH(arg))
+            return MAKE_VBSERROR(VBSE_OBJECT_VARIABLE_NOT_SET);
+        hres = disp_call(This->ctx, V_DISPATCH(arg), DISPID_VALUE, TRUE, &dp, &default_value);
+        if(FAILED(hres))
+            return hres;
+        array_arg = &default_value;
+    }
+
+    switch(V_VT(array_arg)) {
     case VT_VARIANT|VT_ARRAY:
-        sa = V_ARRAY(arg);
+        sa = V_ARRAY(array_arg);
         break;
     case VT_VARIANT|VT_ARRAY|VT_BYREF:
-        sa = *V_ARRAYREF(arg);
+        sa = *V_ARRAYREF(array_arg);
         break;
-    case VT_EMPTY:
-    case VT_NULL:
-        return MAKE_VBSERROR(VBSE_TYPE_MISMATCH);
     default:
-        FIXME("arg %s not supported\n", debugstr_variant(arg));
-        return E_NOTIMPL;
+        VariantClear(&default_value);
+        return MAKE_VBSERROR(VBSE_TYPE_MISMATCH);
     }
 
-    if(!sa)
-        return MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS);
+    if(!sa) {
+        hres = MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS);
+        goto done;
+    }
 
     if(args_cnt == 2) {
         hres = to_int(arg + 1, &dim);
         if(FAILED(hres))
-            return hres;
+            goto done;
     }else {
         dim = 1;
     }
 
     hres = SafeArrayGetUBound(sa, dim, &ubound);
-    if(FAILED(hres))
-        return hres;
+    if(SUCCEEDED(hres))
+        hres = return_int(res, ubound);
 
-    return return_int(res, ubound);
+done:
+    VariantClear(&default_value);
+    return hres;
 }
 
 static HRESULT Global_RGB(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res)
diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c
index ac2bbc64ce64..8f1688229a12 100644
--- a/dlls/vbscript/interp.c
+++ b/dlls/vbscript/interp.c
@@ -1411,6 +1411,13 @@ static HRESULT interp_dim(exec_ctx_t *ctx)
         assert(var != NULL);
         v = &var->v;
         array_ref = &var->array;
+
+        /* A separate ExecuteGlobal can re-encounter a name that was already
+         * Dim'd as an array in an earlier compile unit. Native rejects this
+         * with err 13 regardless of whether the existing array is fixed or
+         * dynamic. Re-Dim'ing an existing scalar (V_VT_EMPTY) is allowed. */
+        if(V_ISARRAY(v))
+            return MAKE_VBSERROR(VBSE_TYPE_MISMATCH);
     }else {
         ref_t ref;
 
@@ -2090,6 +2097,31 @@ static inline void coerce_empty_to_i2(variant_val_t *val)
     val->owned = TRUE;
 }
 
+/* Native And/Or/Imp coerce a BSTR operand to a number when it parses as one,
+ * falling back to a Boolean conversion otherwise. VarXor already does this
+ * internally; VarAnd/VarOr/VarImp do not, so VBScript needs to coerce first. */
+static HRESULT coerce_str_to_num_or_bool(variant_val_t *val)
+{
+    DOUBLE d;
+    HRESULT hres;
+    VARTYPE target;
+
+    if(V_VT(val->v) != VT_BSTR)
+        return S_OK;
+
+    target = SUCCEEDED(VarR8FromStr(V_BSTR(val->v), LOCALE_USER_DEFAULT, 0, &d)) ? VT_I4 : VT_BOOL;
+    VariantInit(&val->store);
+    hres = VariantChangeType(&val->store, val->v, VARIANT_LOCALBOOL, target);
+    if(FAILED(hres))
+        return hres;
+
+    if(val->owned)
+        VariantClear(val->v);
+    val->v = &val->store;
+    val->owned = TRUE;
+    return S_OK;
+}
+
 static HRESULT interp_not(exec_ctx_t *ctx)
 {
     variant_val_t val;
@@ -2127,7 +2159,11 @@ static HRESULT interp_and(exec_ctx_t *ctx)
     if(SUCCEEDED(hres)) {
         coerce_empty_to_i4(&l);
         coerce_empty_to_i4(&r);
-        hres = VarAnd(l.v, r.v, &v);
+        hres = coerce_str_to_num_or_bool(&l);
+        if(SUCCEEDED(hres))
+            hres = coerce_str_to_num_or_bool(&r);
+        if(SUCCEEDED(hres))
+            hres = VarAnd(l.v, r.v, &v);
         release_val(&l);
     }
     release_val(&r);
@@ -2153,7 +2189,11 @@ static HRESULT interp_or(exec_ctx_t *ctx)
     if(SUCCEEDED(hres)) {
         coerce_empty_to_i4(&l);
         coerce_empty_to_i4(&r);
-        hres = VarOr(l.v, r.v, &v);
+        hres = coerce_str_to_num_or_bool(&l);
+        if(SUCCEEDED(hres))
+            hres = coerce_str_to_num_or_bool(&r);
+        if(SUCCEEDED(hres))
+            hres = VarOr(l.v, r.v, &v);
         release_val(&l);
     }
     release_val(&r);
@@ -2241,7 +2281,11 @@ static HRESULT interp_imp(exec_ctx_t *ctx)
         } else {
             coerce_empty_to_i4(&l);
             coerce_empty_to_i4(&r);
-            hres = VarImp(l.v, r.v, &v);
+            hres = coerce_str_to_num_or_bool(&l);
+            if(SUCCEEDED(hres))
+                hres = coerce_str_to_num_or_bool(&r);
+            if(SUCCEEDED(hres))
+                hres = VarImp(l.v, r.v, &v);
         }
         release_val(&l);
     }
diff --git a/dlls/vbscript/lex.c b/dlls/vbscript/lex.c
index 81cc67e037be..db1b8b7b3553 100644
--- a/dlls/vbscript/lex.c
+++ b/dlls/vbscript/lex.c
@@ -17,7 +17,9 @@
  */
 
 #include <assert.h>
+#include <errno.h>
 #include <limits.h>
+#include <locale.h>
 #include <math.h>
 
 #include "vbscript.h"
@@ -266,90 +268,108 @@ static int parse_date_literal(parser_ctx_t *ctx, DATE *ret)
     return tDate;
 }
 
+/* Cached C locale, lazily created and reused for the lifetime of the process.
+ * Used by parse_numeric_literal to feed _wcstod_l for double conversion that
+ * is locale-invariant regardless of the host's runtime locale. */
+static _locale_t c_locale;
+
+static _locale_t get_c_locale(void)
+{
+    _locale_t l;
+
+    if(c_locale)
+        return c_locale;
+
+    if(!(l = _create_locale(LC_ALL, "C")))
+        return NULL;
+    if(InterlockedCompareExchangePointer((void **)&c_locale, l, NULL))
+        _free_locale(l);
+    return c_locale;
+}
+
+void release_c_locale(void)
+{
+    if(c_locale) {
+        _free_locale(c_locale);
+        c_locale = NULL;
+    }
+}
+
 static int parse_numeric_literal(parser_ctx_t *ctx, void **ret)
 {
-    BOOL use_int = TRUE;
+    const WCHAR *start = ctx->ptr;
+    BOOL is_double = FALSE, overflow = FALSE;
     LONGLONG d = 0, hlp;
-    int exp = 0;
+    _locale_t locale;
+    WCHAR stackbuf[64];
+    WCHAR *buf, *endptr;
+    size_t len;
     double r;
 
     if(*ctx->ptr == '0' && !('0' <= ctx->ptr[1] && ctx->ptr[1] <= '9') && ctx->ptr[1] != '.')
         return *ctx->ptr++;
 
+    /* Walk integer digits, accumulating into d while it fits. */
     while(ctx->ptr < ctx->end && is_digit(*ctx->ptr)) {
-        hlp = d*10 + *(ctx->ptr++) - '0';
-        if(d>MAXLONGLONG/10 || hlp<0) {
-            exp++;
-            break;
+        if(!overflow) {
+            hlp = d*10 + *ctx->ptr - '0';
+            if(d > MAXLONGLONG/10 || hlp < 0)
+                overflow = TRUE;
+            else
+                d = hlp;
         }
-        else
-            d = hlp;
-    }
-    while(ctx->ptr < ctx->end && is_digit(*ctx->ptr)) {
-        exp++;
         ctx->ptr++;
     }
 
     if(*ctx->ptr == '.') {
-        use_int = FALSE;
+        is_double = TRUE;
         ctx->ptr++;
-
-        while(ctx->ptr < ctx->end && is_digit(*ctx->ptr)) {
-            hlp = d*10 + *(ctx->ptr++) - '0';
-            if(d>MAXLONGLONG/10 || hlp<0)
-                break;
-
-            d = hlp;
-            exp--;
-        }
         while(ctx->ptr < ctx->end && is_digit(*ctx->ptr))
             ctx->ptr++;
     }
 
     if(*ctx->ptr == 'e' || *ctx->ptr == 'E') {
-        int e = 0, sign = 1;
-
+        is_double = TRUE;
         ctx->ptr++;
-        if(*ctx->ptr == '-') {
-            ctx->ptr++;
-            sign = -1;
-        }else if(*ctx->ptr == '+') {
+        if(*ctx->ptr == '+' || *ctx->ptr == '-')
             ctx->ptr++;
-        }
-
-        if(!is_digit(*ctx->ptr)) {
+        if(!is_digit(*ctx->ptr))
             return lex_error(ctx, MAKE_VBSERROR(VBSE_INVALID_NUMBER));
-        }
-
-        use_int = FALSE;
-
-        do {
-            e = e*10 + *(ctx->ptr++) - '0';
-            if(sign == -1 && -e+exp < -(INT_MAX/100)) {
-                /* The literal will be rounded to 0 anyway. */
-                while(is_digit(*ctx->ptr))
-                    ctx->ptr++;
-                *(double*)ret = 0;
-                return tDouble;
-            }
-
-            if(sign*e + exp > INT_MAX/100) {
-                return lex_error(ctx, MAKE_VBSERROR(VBSE_INVALID_NUMBER));
-            }
-        } while(is_digit(*ctx->ptr));
-
-        exp += sign*e;
+        while(is_digit(*ctx->ptr))
+            ctx->ptr++;
     }
 
-    if(use_int && (LONG)d == d) {
+    if(!is_double && !overflow && (LONG)d == d) {
         *(LONG*)ret = d;
         return tInt;
     }
 
-    r = exp>=0 ? d*pow(10, exp) : d/pow(10, -exp);
-    if(isinf(r)) {
-        return lex_error(ctx, MAKE_VBSERROR(VBSE_INVALID_NUMBER));
+    /* Delegate the real-number conversion to ucrtbase's locale-aware parser
+     * with a C locale, which handles rounding, subnormals, and overflow per
+     * IEEE 754. The lexer above has already determined the literal span; we
+     * just need to hand wcstod a null-terminated copy. Number literals have no
+     * upper length limit (e.g. trailing fractional zeroes), so fall back to a
+     * heap buffer when the stack buffer is too small. */
+    len = ctx->ptr - start;
+    if(len < ARRAY_SIZE(stackbuf)) {
+        buf = stackbuf;
+    }else if(!(buf = malloc((len + 1) * sizeof(WCHAR)))) {
+        return lex_error(ctx, MAKE_VBSERROR(VBSE_OUT_OF_MEMORY));
     }
+    memcpy(buf, start, len * sizeof(WCHAR));
+    buf[len] = 0;
+
+    locale = get_c_locale();
+    if(!locale) {
+        if(buf != stackbuf) free(buf);
+        return lex_error(ctx, MAKE_VBSERROR(VBSE_OUT_OF_MEMORY));
+    }
+    errno = 0;
+    r = _wcstod_l(buf, &endptr, locale);
+    if(buf != stackbuf) free(buf);
+
+    if(errno == ERANGE && isinf(r))
+        return lex_error(ctx, MAKE_VBSERROR(VBSE_INVALID_NUMBER));
 
     *(double*)ret = r;
     return tDouble;
@@ -394,6 +414,35 @@ static int parse_hex_literal(parser_ctx_t *ctx, LONG *ret)
     return tInt;
 }
 
+static int oct_to_int(WCHAR c)
+{
+    if('0' <= c && c <= '7')
+        return c-'0';
+    return -1;
+}
+
+static int parse_oct_literal(parser_ctx_t *ctx, LONG *ret)
+{
+    ULONGLONG l = 0;
+    int d;
+
+    while((d = oct_to_int(*++ctx->ptr)) != -1) {
+        l = l*8 + d;
+        if(l > UINT_MAX) {
+            WARN("overflow in oct literal\n");
+            return 0;
+        }
+    }
+
+    if(*ctx->ptr == '&') {
+        ctx->ptr++;
+        *ret = (LONG)l;
+    }else {
+        *ret = l == (UINT16)l ? (INT16)l : (LONG)l;
+    }
+    return tInt;
+}
+
 static void skip_spaces(parser_ctx_t *ctx)
 {
     while(*ctx->ptr == ' ' || *ctx->ptr == '\t' || *ctx->ptr == '\v' || *ctx->ptr == '\f')
@@ -537,6 +586,8 @@ static int parse_next_token(void *lval, unsigned *loc, parser_ctx_t *ctx)
     case '&':
         if((*++ctx->ptr == 'h' || *ctx->ptr == 'H') && hex_to_int(ctx->ptr[1]) != -1)
             return parse_hex_literal(ctx, lval);
+        if((*ctx->ptr == 'o' || *ctx->ptr == 'O') && oct_to_int(ctx->ptr[1]) != -1)
+            return parse_oct_literal(ctx, lval);
         return '&';
     case '=':
         switch(*++ctx->ptr) {
diff --git a/dlls/vbscript/tests/api.vbs b/dlls/vbscript/tests/api.vbs
index 8cbd5824b391..1a8e79c619e4 100644
--- a/dlls/vbscript/tests/api.vbs
+++ b/dlls/vbscript/tests/api.vbs
@@ -332,6 +332,9 @@ Call ok(Lbound(arr2, 1) = 0, "Lbound(x) = " & Lbound(x))
 Call ok(Lbound(arr2, 2) = 0, "Lbound(x) = " & Lbound(x))
 
 sub testLBoundError()
+    Dim nothingObj : Set nothingObj = Nothing
+    Dim emptyObj   : Set emptyObj   = new EmptyClass
+    Dim dictObj    : Set dictObj    = CreateObject("Scripting.Dictionary")
     on error resume next
     call Err.clear()
     call LBound()
@@ -343,6 +346,27 @@ sub testLBoundError()
     call LBound(Null)
     call ok(Err.number = 13, "Err.number = " & Err.number)
     call Err.clear()
+    call LBound(42)
+    call ok(Err.number = 13, "LBound(int) Err.number = " & Err.number)
+    call Err.clear()
+    call LBound("str")
+    call ok(Err.number = 13, "LBound(string) Err.number = " & Err.number)
+    call Err.clear()
+    call LBound(True)
+    call ok(Err.number = 13, "LBound(bool) Err.number = " & Err.number)
+    call Err.clear()
+    call LBound(3.14)
+    call ok(Err.number = 13, "LBound(double) Err.number = " & Err.number)
+    call Err.clear()
+    call LBound(nothingObj)
+    call ok(Err.number = 91, "LBound(Nothing) Err.number = " & Err.number)
+    call Err.clear()
+    call LBound(emptyObj)
+    call ok(Err.number = 438, "LBound(no-default-object) Err.number = " & Err.number)
+    call Err.clear()
+    call LBound(dictObj)
+    call ok(Err.number = 450, "LBound(default-needs-arg) Err.number = " & Err.number)
+    call Err.clear()
     call LBound(arr, 1, 2)
     call ok(Err.number = 450, "Err.number = " & Err.number)
     if isEnglishLang then call ok(Err.description = "Wrong number of arguments or invalid property assignment", _
@@ -350,6 +374,9 @@ sub testLBoundError()
 end sub
 
 sub testUBoundError()
+    Dim nothingObj : Set nothingObj = Nothing
+    Dim emptyObj   : Set emptyObj   = new EmptyClass
+    Dim dictObj    : Set dictObj    = CreateObject("Scripting.Dictionary")
     on error resume next
     call Err.clear()
     call UBound()
@@ -361,6 +388,27 @@ sub testUBoundError()
     call UBound(Null)
     call ok(Err.number = 13, "Err.number = " & Err.number)
     call Err.clear()
+    call UBound(42)
+    call ok(Err.number = 13, "UBound(int) Err.number = " & Err.number)
+    call Err.clear()
+    call UBound("str")
+    call ok(Err.number = 13, "UBound(string) Err.number = " & Err.number)
+    call Err.clear()
+    call UBound(True)
+    call ok(Err.number = 13, "UBound(bool) Err.number = " & Err.number)
+    call Err.clear()
+    call UBound(3.14)
+    call ok(Err.number = 13, "UBound(double) Err.number = " & Err.number)
+    call Err.clear()
+    call UBound(nothingObj)
+    call ok(Err.number = 91, "UBound(Nothing) Err.number = " & Err.number)
+    call Err.clear()
+    call UBound(emptyObj)
+    call ok(Err.number = 438, "UBound(no-default-object) Err.number = " & Err.number)
+    call Err.clear()
+    call UBound(dictObj)
+    call ok(Err.number = 450, "UBound(default-needs-arg) Err.number = " & Err.number)
+    call Err.clear()
     call UBound(arr, 1, 2)
     call ok(Err.number = 450, "Err.number = " & Err.number)
     if isEnglishLang then call ok(Err.description = "Wrong number of arguments or invalid property assignment", _
@@ -370,6 +418,35 @@ end sub
 call testLBoundError()
 call testUBoundError()
 
+Class ArrayDefaultClass
+    Public Default Function GetArr
+        GetArr = Array(10, 20, 30)
+    End Function
+End Class
+
+Class ScalarDefaultClass
+    Public Default Function GetIt
+        GetIt = 42
+    End Function
+End Class
+
+sub testBoundDispatchDefault()
+    Dim arrObj    : Set arrObj    = new ArrayDefaultClass
+    Dim scalarObj : Set scalarObj = new ScalarDefaultClass
+
+    call ok(LBound(arrObj) = 0, "LBound(array-default) = " & LBound(arrObj))
+    call ok(UBound(arrObj) = 2, "UBound(array-default) = " & UBound(arrObj))
+
+    on error resume next
+    call LBound(scalarObj)
+    call ok(Err.number = 13, "LBound(scalar-default) Err.number = " & Err.number)
+    Err.Clear()
+    call UBound(scalarObj)
+    call ok(Err.number = 13, "UBound(scalar-default) Err.number = " & Err.number)
+end sub
+
+call testBoundDispatchDefault()
+
 sub testBoundUninitArray()
     Dim u()
     on error resume next
diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs
index d8d612bd389d..acdd992cbcf0 100644
--- a/dlls/vbscript/tests/lang.vbs
+++ b/dlls/vbscript/tests/lang.vbs
@@ -87,6 +87,29 @@ Call ok(&h0& = 0, "&h0& <> 0")
 Call ok(&h00 = 0, "&h00 <> 0")
 Call ok(&h000000000 = 0, "&h000000000 <> 0")
 
+' Octal literals (&O...)
+Call ok(&O0 = 0, "&O0 <> 0")
+Call ok(&O17 = 15, "&O17 <> 15")
+Call ok(&o17 = 15, "&o17 (lowercase) <> 15")
+Call ok(&O77 = 63, "&O77 <> 63")
+Call ok(&O777 = 511, "&O777 <> 511")
+Call ok(&O177777 = -1, "&O177777 <> -1")
+Call ok(&O200000 = 65536, "&O200000 <> 65536")
+Call ok(&O17777777777 = 2147483647, "&O17777777777 <> 2147483647")
+Call ok(&O37777777777 = -1, "&O37777777777 <> -1")
+Call ok(&O17& = 15, "&O17& <> 15")
+Call ok(&O177777& = 65535, "&O177777& <> 65535")
+Call ok(getVT(&O77) = "VT_I2", "getVT(&O77) is not VT_I2")
+Call ok(getVT(&O200000) = "VT_I4", "getVT(&O200000) is not VT_I4")
+Call ok(getVT(&O177777&) = "VT_I4", "getVT(&O177777&) is not VT_I4")
+
+sub testOctalLiteralErrors()
+    Dim oct
+    on error resume next
+    Err.Clear : oct = Eval("&O8") : call ok(Err.number = 1002, "&O8 should be syntax error, got err=" & Err.number)
+end sub
+call testOctalLiteralErrors()
+
 ' Test concat when no space and var begins with h
 hi = "y"
 x = "x" &hi
@@ -439,6 +462,36 @@ Call ok(getVT(# 1/1/2011 #) = "VT_DATE", "getVT(# 1/1/2011 #) is not VT_DATE")
 Call ok(getVT(1e2) = "VT_R8", "getVT(1e2) is not VT_R8")
 Call ok(getVT(1e0) = "VT_R8", "getVT(1e0) is not VT_R8")
 Call ok(getVT(0.1e2) = "VT_R8", "getVT(0.1e2) is not VT_R8")
+' Subnormal doubles: literals smaller than DBL_MIN (~1e-308) parse as positive
+' subnormals down to ~5e-324; anything smaller rounds to zero.
+Call ok(1e-309 > 0, "1e-309 should be subnormal positive, got " & 1e-309)
+Call ok(1e-320 > 0, "1e-320 should be subnormal positive, got " & 1e-320)
+Call ok(5e-324 > 0, "5e-324 should be subnormal positive, got " & 5e-324)
+Call ok(-1e-309 < 0, "-1e-309 should be subnormal negative, got " & -1e-309)
+Call ok(getVT(1e-309) = "VT_R8", "getVT(1e-309) = " & getVT(1e-309))
+Call ok(1e-400 = 0, "1e-400 should round to 0, got " & 1e-400)
+' Boundary at DBL_MAX: the parser must not collapse e308 to infinity.
+Call ok(1e308 > 0, "1e308 should be finite positive, got " & 1e308)
+Call ok(1.7976931348623157e308 > 1e307, "DBL_MAX should be larger than 1e307")
+Call ok(getVT(1.7976931348623157e308) = "VT_R8", "getVT(DBL_MAX) is not VT_R8")
+Call ok(1.00000000000000000000000000000000000000000000000000000000000000000000000000000000 = 1, "long 1.0 literal should equal 1")
+Call ok(0.00000000000000000000000000000000000000000000000000000000000000000000000000000001 > 0, "long fractional literal should be positive subnormal/small")
+Call ok(00000000000000000000000000000000000000000000000000000000000000000000000000000001 = 1, "long leading-zero integer literal should equal 1")
+Call ok(getVT(00000000000000000000000000000000000000000000000000000000000000000000000000000001) = "VT_I2", "long leading-zero integer literal should stay VT_I2")
+Call ok(00000000000000000000000000000000000000000000000000000000000000000000000000000000.5 = 0.5, "long leading-zero decimal literal should equal 0.5")
+
+' Above DBL_MAX must raise err 1031 (Invalid number) at compile time, not
+' silently overflow to infinity.
+Sub TestNumericOverflow
+    On Error Resume Next
+    Err.Clear : Execute "Dim r : r = 1e309"
+    Call ok(Err.Number = 1031, "1e309 should err 1031, got " & Err.Number)
+    Err.Clear : Execute "Dim r : r = -1e309"
+    Call ok(Err.Number = 1031, "-1e309 should err 1031, got " & Err.Number)
+    Err.Clear : Execute "Dim r : r = 1.8e308"
+    Call ok(Err.Number = 1031, "1.8e308 should err 1031, got " & Err.Number)
+End Sub
+Call TestNumericOverflow
 Call ok(getVT(1 & 100000) = "VT_BSTR", "getVT(1 & 100000) is not VT_BSTR")
 Call ok(getVT(-empty) = "VT_I2", "getVT(-empty) = " & getVT(-empty))
 Call ok(getVT(-null) = "VT_NULL", "getVT(-null) = " & getVT(-null))
@@ -563,6 +616,34 @@ Call ok(getVT(Empty Imp Null) = "VT_I4",     "getVT(Empty Imp Null) = " & getVT(
 Call ok((Not Empty) = -1,                    "Not Empty is not -1")
 Call ok(getVT(Not Empty) = "VT_I4",          "getVT(Not Empty) = " & getVT(Not Empty))
 
+' Logical/bitwise ops with BSTR operands coerce the string to a number when
+' parseable (matching VarXor), giving a Long result instead of treating the
+' string as a Boolean.
+Call ok(("1" And "2") = 0,                   """1"" And ""2"" is not 0")
+Call ok(("5" And "3") = 1,                   """5"" And ""3"" is not 1")
+Call ok(getVT("1" And "2") = "VT_I4",        "getVT(""1"" And ""2"") = " & getVT("1" And "2"))
+Call ok(("1" Or "2") = 3,                    """1"" Or ""2"" is not 3")
+Call ok(("5" Or "3") = 7,                    """5"" Or ""3"" is not 7")
+Call ok(getVT("1" Or "2") = "VT_I4",         "getVT(""1"" Or ""2"") = " & getVT("1" Or "2"))
+Call ok(("1" Imp "2") = -2,                  """1"" Imp ""2"" is not -2")
+Call ok(getVT("1" Imp "2") = "VT_I4",        "getVT(""1"" Imp ""2"") = " & getVT("1" Imp "2"))
+
+' Mixed BSTR + numeric stays Long.
+Call ok((5 And "3") = 1,                     "5 And ""3"" is not 1")
+Call ok(("5" And 3) = 1,                     """5"" And 3 is not 1")
+Call ok(getVT(5 And "3") = "VT_I4",          "getVT(5 And ""3"") = " & getVT(5 And "3"))
+
+' BSTR with non-numeric content falls back to Boolean conversion, which
+' fails with type mismatch for arbitrary text.
+Sub testLogicalBstrErr
+    Dim r
+    on error resume next
+    Err.Clear : r = "abc" And "2" : call ok(Err.Number = 13, """abc"" And ""2"" err=" & Err.Number)
+    Err.Clear : r = "abc" Or  "2" : call ok(Err.Number = 13, """abc"" Or ""2"" err=" & Err.Number)
+    Err.Clear : r = "abc" Imp "2" : call ok(Err.Number = 13, """abc"" Imp ""2"" err=" & Err.Number)
+End Sub
+Call testLogicalBstrErr
+
 ' Arithmetic binary ops coerce Empty to VT_I2 0 — narrower than the logical
 ' family — so the widening picks up whichever side has the larger numeric
 ' type and the result reflects that.
@@ -2799,6 +2880,103 @@ sub TestRedimInputArg
 end sub
 Call TestRedimInputArg
 
+sub TestExecuteGlobalRedim
+    on error resume next
+
+    ' Dim arr() already declared as a dynamic array in a prior compile unit.
+    err.clear : ExecuteGlobal "Dim egDynArr()"
+    call ok(err.number = 0, "first Dim egDynArr() err=" & err.number)
+
+    err.clear : ExecuteGlobal "Dim egDynArr()"
+    call ok(err.number = 13, "second Dim egDynArr() err=" & err.number)
+
+    err.clear : ExecuteGlobal "Dim egDynArr() : ReDim egDynArr(5)"
+    call ok(err.number = 13, "Dim+ReDim egDynArr() err=" & err.number)
+
+    ' Dim arr(N) already declared as a fixed array in a prior compile unit.
+    err.clear : ExecuteGlobal "Dim egFixArr(2)"
+    call ok(err.number = 0, "first Dim egFixArr(2) err=" & err.number)
+
+    err.clear : ExecuteGlobal "Dim egFixArr(2)"
+    call ok(err.number = 13, "second Dim egFixArr(2) err=" & err.number)
+
+    err.clear : ExecuteGlobal "Dim egFixArr()"
+    call ok(err.number = 13, "second Dim egFixArr() err=" & err.number)
+
+    ' Re-Dim'ing a previously scalar Dim is allowed.
+    err.clear : ExecuteGlobal "Dim egScalar"
+    call ok(err.number = 0, "first Dim egScalar err=" & err.number)
+
+    err.clear : ExecuteGlobal "Dim egScalar"
+    call ok(err.number = 0, "second Dim egScalar (scalar) err=" & err.number)
+
+    err.clear : ExecuteGlobal "Dim egScalar()"
+    call ok(err.number = 0, "second Dim egScalar() (array) err=" & err.number)
+end sub
+Call TestExecuteGlobalRedim
+
+Class FixedClassArr
+    Private mArr(2)
+    Public LastErr
+    Public Sub Resize(n)
+        On Error Resume Next
+        Err.Clear : ReDim mArr(n) : LastErr = Err.Number
+    End Sub
+    Public Sub ResizePreserve(n)
+        On Error Resume Next
+        Err.Clear : ReDim Preserve mArr(n) : LastErr = Err.Number
+    End Sub
+End Class
+
+Class FixedClassArr2D
+    Private mArr(2, 3)
+    Public LastErr
+    Public Sub Resize(a, b)
+        On Error Resume Next
+        Err.Clear : ReDim mArr(a, b) : LastErr = Err.Number
+    End Sub
+End Class
+
+Class DynamicClassArr
+    Private mArr()
+    Public LastErr
+    Public Function UB : UB = UBound(mArr) : End Function
+    Public Sub Resize(n)
+        On Error Resume Next
+        Err.Clear : ReDim mArr(n) : LastErr = Err.Number
+    End Sub
+End Class
+
+Class ScalarClassMember
+    Private mArr
+    Public LastErr
+    Public Function UB : UB = UBound(mArr) : End Function
+    Public Sub Resize(n)
+        On Error Resume Next
+        Err.Clear : ReDim mArr(n) : LastErr = Err.Number
+    End Sub
+End Class
+
+dim cFix : Set cFix = New FixedClassArr
+cFix.Resize 5
+call ok(cFix.LastErr = 10, "ReDim fixed class member err = " & cFix.LastErr)
+cFix.ResizePreserve 5
+call ok(cFix.LastErr = 10, "ReDim Preserve fixed class member err = " & cFix.LastErr)
+
+dim cFix2D : Set cFix2D = New FixedClassArr2D
+cFix2D.Resize 5, 7
+call ok(cFix2D.LastErr = 10, "ReDim fixed 2D class member err = " & cFix2D.LastErr)
+
+dim cDyn : Set cDyn = New DynamicClassArr
+cDyn.Resize 5
+call ok(cDyn.LastErr = 0, "ReDim dynamic class member err = " & cDyn.LastErr)
+call ok(cDyn.UB = 5, "ReDim dynamic class member UB = " & cDyn.UB)
+
+dim cScalar : Set cScalar = New ScalarClassMember
+cScalar.Resize 5
+call ok(cScalar.LastErr = 0, "ReDim scalar->array class member err = " & cScalar.LastErr)
+call ok(cScalar.UB = 5, "ReDim scalar->array class member UB = " & cScalar.UB)
+
 sub TestReDimList
     dim x, y
 
@@ -4284,7 +4462,30 @@ Call ok(Err.Number = 11, "division by zero: err.number = " & Err.Number)
 Err.Clear
 Dim nullResult
 nullResult = CLng(Null)
-todo_wine_ok Err.Number = 94, "CLng(Null): err.number = " & Err.Number
+call ok(Err.Number = 94, "CLng(Null): err.number = " & Err.Number)
+
+' Each Cxxx coercion raises err 94 on Null and err 91 on Nothing.
+Sub testCoerceNullNothing
+    Dim nothingObj : Set nothingObj = Nothing
+    on error resume next
+
+    Err.Clear : call CInt(Null)        : call ok(Err.Number = 94, "CInt(Null) err=" & Err.Number)
+    Err.Clear : call CInt(nothingObj)  : call ok(Err.Number = 91, "CInt(Nothing) err=" & Err.Number)
+    Err.Clear : call CLng(nothingObj)  : call ok(Err.Number = 91, "CLng(Nothing) err=" & Err.Number)
+    Err.Clear : call CBool(Null)       : call ok(Err.Number = 94, "CBool(Null) err=" & Err.Number)
+    Err.Clear : call CBool(nothingObj) : call ok(Err.Number = 91, "CBool(Nothing) err=" & Err.Number)
+    Err.Clear : call CByte(Null)       : call ok(Err.Number = 94, "CByte(Null) err=" & Err.Number)
+    Err.Clear : call CByte(nothingObj) : call ok(Err.Number = 91, "CByte(Nothing) err=" & Err.Number)
+    Err.Clear : call CDbl(Null)        : call ok(Err.Number = 94, "CDbl(Null) err=" & Err.Number)
+    Err.Clear : call CDbl(nothingObj)  : call ok(Err.Number = 91, "CDbl(Nothing) err=" & Err.Number)
+    Err.Clear : call CSng(Null)        : call ok(Err.Number = 94, "CSng(Null) err=" & Err.Number)
+    Err.Clear : call CSng(nothingObj)  : call ok(Err.Number = 91, "CSng(Nothing) err=" & Err.Number)
+    Err.Clear : call CCur(Null)        : call ok(Err.Number = 94, "CCur(Null) err=" & Err.Number)
+    Err.Clear : call CCur(nothingObj)  : call ok(Err.Number = 91, "CCur(Nothing) err=" & Err.Number)
+    Err.Clear : call CDate(nothingObj) : call ok(Err.Number = 91, "CDate(Nothing) err=" & Err.Number)
+    Err.Clear : call CStr(nothingObj)  : call ok(Err.Number = 91, "CStr(Nothing) err=" & Err.Number)
+End Sub
+Call testCoerceNullNothing
 
 ' Error 429: ActiveX component can't create object
 Err.Clear
diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c
index c9fd66588a55..d9d7084a8871 100644
--- a/dlls/vbscript/tests/run.c
+++ b/dlls/vbscript/tests/run.c
@@ -148,6 +148,8 @@ DEFINE_EXPECT(OnLeaveScript);
 #define DISPID_GLOBAL_THROWEXCEPTION  1027
 #define DISPID_GLOBAL_ISARRAYFIXED    1028
 #define DISPID_GLOBAL_MAXCHARSIZE     1029
+#define DISPID_GLOBAL_INVOKEDISP      1030
+#define DISPID_GLOBAL_INVOKEMETHOD    1031
 
 #define DISPID_TESTOBJ_PROPGET      2000
 #define DISPID_TESTOBJ_PROPPUT      2001
@@ -1237,6 +1239,8 @@ static HRESULT WINAPI Global_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD
         { L"MaxCharSize",     DISPID_GLOBAL_MAXCHARSIZE },
         { L"firstDayOfWeek",  DISPID_GLOBAL_WEEKSTARTDAY },
         { L"globalCallback",  DISPID_GLOBAL_GLOBALCALLBACK },
+        { L"invokeDisp",      DISPID_GLOBAL_INVOKEDISP },
+        { L"invokeMethod",    DISPID_GLOBAL_INVOKEMETHOD },
         { L"testObj",         DISPID_GLOBAL_TESTOBJ },
         { L"collectionObj" ,  DISPID_GLOBAL_COLLOBJ },
         { L"vbvar",           DISPID_GLOBAL_VBVAR, REF_EXPECT(global_vbvar_d) },
@@ -1362,6 +1366,63 @@ static HRESULT WINAPI Global_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid,
         V_I4(pvarRes) = MaxCharSize;
         return S_OK;
 
+    case DISPID_GLOBAL_INVOKEDISP: {
+        DISPPARAMS params = { NULL, NULL, 0, 0 };
+        VARIANT *arg = pdp->rgvarg;
+        EXCEPINFO ei;
+        HRESULT hr;
+
+        ok(wFlags == INVOKE_FUNC || wFlags == (INVOKE_FUNC|INVOKE_PROPERTYGET), "wFlags = %x\n", wFlags);
+        ok(pdp != NULL, "pdp == NULL\n");
+        ok(pdp->cArgs == 1, "cArgs = %d\n", pdp->cArgs);
+        if(V_VT(arg) == (VT_VARIANT|VT_BYREF))
+            arg = V_VARIANTREF(arg);
+        ok(V_VT(arg) == VT_DISPATCH, "V_VT(arg) = %d\n", V_VT(arg));
+
+        /* Mimic an external object invoking a function reference back into the
+         * engine while a script frame is on the stack. With the host leaving
+         * the error unhandled, native returns SCRIPT_E_RECORDED to the caller;
+         * Wine returns the raw error code instead. */
+        memset(&ei, 0, sizeof(ei));
+        hr = IDispatch_Invoke(V_DISPATCH(arg), DISPID_VALUE, &IID_NULL, 0,
+                              DISPATCH_METHOD, &params, NULL, &ei, NULL);
+        todo_wine ok(hr == SCRIPT_E_RECORDED, "inner Invoke returned %08lx\n", hr);
+        return S_OK;
+    }
+
+    case DISPID_GLOBAL_INVOKEMETHOD: {
+        DISPPARAMS params = { NULL, NULL, 0, 0 };
+        VARIANT *arg = pdp->rgvarg;
+        IDispatchEx *dispex;
+        EXCEPINFO ei;
+        VARIANT v;
+        DISPID did;
+        BSTR str;
+        HRESULT hr;
+
+        ok(pdp->cArgs == 1, "cArgs = %d\n", pdp->cArgs);
+        if(V_VT(arg) == (VT_VARIANT|VT_BYREF))
+            arg = V_VARIANTREF(arg);
+        ok(V_VT(arg) == VT_DISPATCH, "V_VT(arg) = %d\n", V_VT(arg));
+
+        hr = IDispatch_QueryInterface(V_DISPATCH(arg), &IID_IDispatchEx, (void**)&dispex);
+        ok(hr == S_OK, "QueryInterface(IDispatchEx) returned %08lx\n", hr);
+        str = SysAllocString(L"raiser");
+        hr = IDispatchEx_GetDispID(dispex, str, fdexNameCaseInsensitive, &did);
+        ok(hr == S_OK, "GetDispID(raiser) returned %08lx\n", hr);
+        SysFreeString(str);
+
+        /* As above, but for a class method invoked by an external object. With
+         * the host leaving the error unhandled, native returns DISP_E_EXCEPTION
+         * to the caller; Wine returns the raw error code instead. */
+        memset(&ei, 0, sizeof(ei));
+        V_VT(&v) = VT_EMPTY;
+        hr = IDispatchEx_InvokeEx(dispex, did, 0, DISPATCH_METHOD, &params, &v, &ei, NULL);
+        todo_wine ok(hr == DISP_E_EXCEPTION, "inner InvokeEx returned %08lx\n", hr);
+        IDispatchEx_Release(dispex);
+        return S_OK;
+    }
+
     case DISPID_GLOBAL_WEEKSTARTDAY:
         V_VT(pvarRes) = VT_I4;
         V_I4(pvarRes) = first_day_of_week;
@@ -2965,6 +3026,12 @@ static void test_parse_errors(void)
             1, 4,
             NULL, S_OK, 1041
         },
+        {
+            /* A global Sub collides with a global Dim of the same name - error 1041 */
+            L"Dim s\nSub S\nEnd Sub\n",
+            1, 4,
+            NULL, S_OK, 1041
+        },
         {
             /* Expected identifier - error 1010 */
             L"Dim If\n",
@@ -3514,6 +3581,99 @@ static void test_parse_errors(void)
     error_source_line = NULL;
 }
 
+static void test_redefine_scope(void)
+{
+    static const WCHAR *valid[] = {
+        /* a local Dim may shadow a global Sub */
+        L"Sub S\nEnd Sub\nSub Other\nDim s\nEnd Sub\n",
+        /* a local Const may shadow a global Sub */
+        L"Sub S\nEnd Sub\nSub Other\nConst s = 1\nEnd Sub\n",
+        /* a Class name does not collide with a local Dim of another Sub */
+        L"Class S\nEnd Class\nSub Other\nDim s\nEnd Sub\n",
+        L"Sub Other\nDim s\nEnd Sub\nClass S\nEnd Class\n",
+    };
+    /* A class member lives in a separate namespace, so its name may collide
+     * with a global Dim or Const. Each script also calls the member to prove
+     * it stays usable. */
+    static const WCHAR *valid_class_member[] = {
+        L"Class C\nPublic Function M\nM = 42\nEnd Function\nEnd Class\n"
+        L"Dim M\nDim o : Set o = New C\nCall ok(o.M = 42, \"o.M = \" & o.M)\n",
+        L"Class C\nPublic Function M\nM = 42\nEnd Function\nEnd Class\n"
+        L"Const M = 7\nDim o : Set o = New C\nCall ok(o.M = 42, \"o.M = \" & o.M)\n",
+    };
+    HRESULT hres;
+    unsigned i;
+
+    for (i = 0; i < ARRAY_SIZE(valid); i++) {
+        hres = parse_script_wr(valid[i]);
+        ok(hres == S_OK, "[%u] parse returned %08lx\n", i, hres);
+    }
+
+    for (i = 0; i < ARRAY_SIZE(valid_class_member); i++) {
+        hres = parse_script_wr(valid_class_member[i]);
+        ok(hres == S_OK, "[%u] class member parse returned %08lx\n", i, hres);
+    }
+}
+
+static void test_getref_error_reporting(void)
+{
+    /* An error raised inside a function reference obtained from GetRef, when
+     * the reference is called from script under the caller's On Error Resume
+     * Next, must propagate to that caller rather than be reported to the host. */
+    static const WCHAR *src =
+        L"Dim cb : Set cb = GetRef(\"RaisesError\")\n"
+        L"Sub CallIndirect\n"
+        L"    On Error Resume Next\n"
+        L"    cb\n"
+        L"    On Error Goto 0\n"
+        L"End Sub\n"
+        L"Sub RaisesError\n"
+        L"    Err.Raise 5\n"
+        L"End Sub\n"
+        L"CallIndirect\n";
+    HRESULT hres;
+
+    SET_EXPECT(OnScriptError);
+    hres = parse_script_wr(src);
+    ok(hres == S_OK, "parse_script_wr returned %08lx\n", hres);
+    ok(!called_OnScriptError,
+        "error in a GetRef reference under the caller's On Error Resume Next was reported to the host\n");
+    CLEAR_CALLED(OnScriptError);
+}
+
+static void test_getref_external_caller_error(void)
+{
+    static const WCHAR *src =
+        L"Dim cb : Set cb = GetRef(\"RaisesError\")\n"
+        L"Sub RaisesError\n"
+        L"    Err.Raise 5\n"
+        L"End Sub\n"
+        L"Call invokeDisp(cb)\n";
+    HRESULT hres;
+
+    SET_EXPECT(OnScriptError);
+    hres = parse_script_wr(src);
+    ok(hres == S_OK, "parse_script_wr returned %08lx\n", hres);
+    CHECK_CALLED(OnScriptError);
+}
+
+static void test_external_caller_method_error(void)
+{
+    static const WCHAR *src =
+        L"Class C\n"
+        L"    Public Sub raiser\n"
+        L"        Err.Raise 5\n"
+        L"    End Sub\n"
+        L"End Class\n"
+        L"Call invokeMethod(new C)\n";
+    HRESULT hres;
+
+    SET_EXPECT(OnScriptError);
+    hres = parse_script_wr(src);
+    ok(hres == S_OK, "parse_script_wr returned %08lx\n", hres);
+    CHECK_CALLED(OnScriptError);
+}
+
 static void test_msgbox(void)
 {
     HRESULT hres;
@@ -4210,6 +4370,10 @@ static void run_tests(void)
     test_isexpression();
     test_option_explicit_errors();
     test_parse_errors();
+    test_redefine_scope();
+    test_getref_error_reporting();
+    test_getref_external_caller_error();
+    test_external_caller_method_error();
     test_parse_context();
     test_callbacks();
     test_multiple_parse();
diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c
index 1ea228c8aca9..38e4ed140abe 100644
--- a/dlls/vbscript/vbdisp.c
+++ b/dlls/vbscript/vbdisp.c
@@ -741,6 +741,7 @@ HRESULT create_vbdisp(const class_desc_t *desc, vbdisp_t **ret)
                     hres = E_OUTOFMEMORY;
                     break;
                 }
+                vbdisp->arrays[i]->fFeatures |= FADF_FIXEDSIZE | FADF_STATIC;
             }
 
             if(SUCCEEDED(hres)) {
@@ -856,6 +857,10 @@ static HRESULT WINAPI FuncRef_Invoke(IDispatch *iface, DISPID dispIdMember, REFI
     if(dispIdMember != DISPID_VALUE)
         return DISP_E_MEMBERNOTFOUND;
 
+    /* The engine runs its own references through disp_call as internal calls;
+     * reaching this vtable entry means the caller is outside the engine, so the
+     * reference is entered as an external caller and errors are reported to the
+     * host rather than propagated as an HRESULT. */
     return exec_script(This->ctx, TRUE, This->func, NULL, pDispParams, pVarResult);
 }
 
@@ -869,6 +874,11 @@ static const IDispatchVtbl FuncRefVtbl = {
     FuncRef_Invoke
 };
 
+static inline FuncRef *unsafe_funcref_from_IDispatch(IDispatch *iface)
+{
+    return iface->lpVtbl == &FuncRefVtbl ? FuncRef_from_IDispatch(iface) : NULL;
+}
+
 HRESULT create_func_ref(script_ctx_t *ctx, function_t *func, IDispatch **ret)
 {
     FuncRef *ref;
@@ -2008,6 +2018,7 @@ HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, BOOL is_call, D
     const WORD flags = DISPATCH_METHOD|(retv ? DISPATCH_PROPERTYGET : 0);
     IDispatchEx *dispex;
     vbdisp_t *vbdisp;
+    FuncRef *funcref;
     EXCEPINFO ei;
     HRESULT hres;
 
@@ -2019,6 +2030,10 @@ HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, BOOL is_call, D
     if(vbdisp && vbdisp->desc && vbdisp->desc->ctx == ctx)
         return invoke_vbdisp(vbdisp, id, flags, FALSE, is_call, dp, retv);
 
+    funcref = unsafe_funcref_from_IDispatch(disp);
+    if(funcref && funcref->ctx == ctx && id == DISPID_VALUE)
+        return exec_script(ctx, FALSE, funcref->func, NULL, dp, retv);
+
     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
     if(SUCCEEDED(hres)) {
         hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, dp, retv, &ei, &ctx->vbcaller->IServiceProvider_iface);
diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h
index eda5d8740b4e..7e902badb0f5 100644
--- a/dlls/vbscript/vbscript.h
+++ b/dlls/vbscript/vbscript.h
@@ -436,6 +436,7 @@ HRESULT get_builtin_id(BuiltinDisp*,const WCHAR*,DISPID*);
 HRESULT array_access(SAFEARRAY *array, DISPPARAMS *dp, VARIANT **ret);
 
 void release_regexp_typelib(void);
+void release_c_locale(void);
 HRESULT get_dispatch_typeinfo(ITypeInfo**);
 
 static inline BOOL is_int32(double d)
diff --git a/dlls/vbscript/vbscript_main.c b/dlls/vbscript/vbscript_main.c
index fe63c7660a72..4f424a977277 100644
--- a/dlls/vbscript/vbscript_main.c
+++ b/dlls/vbscript/vbscript_main.c
@@ -272,6 +272,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
         if (lpv) break;
         if (dispatch_typeinfo) ITypeInfo_Release(dispatch_typeinfo);
         release_regexp_typelib();
+        release_c_locale();
     }
 
     return TRUE;
diff --git a/dlls/virtdisk/Makefile.in b/dlls/virtdisk/Makefile.in
index d845f5c28d27..6c204009f11e 100644
--- a/dlls/virtdisk/Makefile.in
+++ b/dlls/virtdisk/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = virtdisk.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	virtdisk_main.c
diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in
index 9ca6421c2990..b269ffcdc7dd 100644
--- a/dlls/vulkan-1/Makefile.in
+++ b/dlls/vulkan-1/Makefile.in
@@ -2,8 +2,6 @@ MODULE    = vulkan-1.dll
 IMPORTS   = user32
 IMPORTLIB = vulkan-1
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine Vulkan Loader"
 # Set to 1.0.0 so the official Vulkan runtime installer will replace this file
 VER_PRODUCTVERSION      = 1,0,0,0
diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c
index 59f5e89d8c88..9e1036cdc26a 100644
--- a/dlls/wbemprox/builtin.c
+++ b/dlls/wbemprox/builtin.c
@@ -1929,7 +1929,7 @@ static UINT64 get_total_virtual_memory(void)
 
     status.dwLength = sizeof(status);
     if (!GlobalMemoryStatusEx( &status )) return 1024 * 1024 * 1024;
-    return status.ullTotalVirtual;
+    return status.ullTotalPageFile;
 }
 
 static UINT64 get_available_virtual_memory(void)
@@ -1938,7 +1938,7 @@ static UINT64 get_available_virtual_memory(void)
 
     status.dwLength = sizeof(status);
     if (!GlobalMemoryStatusEx( &status )) return 1024 * 1024 * 1024;
-    return status.ullAvailVirtual;
+    return status.ullAvailPageFile;
 }
 
 static WCHAR *get_computername(void)
diff --git a/dlls/win32u/class.c b/dlls/win32u/class.c
index 9936e31f8046..708e0f04f0cf 100644
--- a/dlls/win32u/class.c
+++ b/dlls/win32u/class.c
@@ -47,14 +47,8 @@ typedef struct tagCLASS
 {
     struct list  entry;         /* Entry in class list */
     BOOL         local;         /* Local class? */
-    WNDPROC      winproc;       /* Window procedure */
     struct dce  *dce;           /* Opaque pointer to class DCE */
-    HICON        hIcon;         /* Default icon */
-    HICON        hIconSm;       /* Default small icon */
-    HICON        hIconSmIntern; /* Internal small icon, derived from hIcon */
-    HCURSOR      hCursor;       /* Default cursor */
-    HBRUSH       hbrBackground; /* Default background */
-    struct client_menu_name menu_name; /* Default menu name */
+    HICON        icon_internal; /* internal small icon, derived from hIcon */
     const shared_object_t *shared; /* class object in session shared memory */
 } CLASS;
 
@@ -66,9 +60,109 @@ struct builtin_class_descr
     INT        extra;     /* window extra bytes */
     ULONG_PTR  cursor;    /* cursor id */
     HBRUSH     brush;     /* brush or system color */
-    enum ntuser_client_procs proc;
+    ATOM       atom;      /* class atom */
 };
 
+static struct builtin_class_descr builtin_classes[] =
+{
+    [NTUSER_WNDPROC_SCROLLBAR] =
+    {
+        .name = "ScrollBar",
+        .style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC,
+        .extra = sizeof(struct scroll_bar_win_data),
+        .cursor = IDC_ARROW,
+    },
+    [NTUSER_WNDPROC_MESSAGE] =
+    {
+        .name = "Message",
+    },
+    [NTUSER_WNDPROC_MENU] =
+    {
+        .name = "#32768", /* POPUPMENU_CLASS_ATOM */
+        .style = CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS,
+        .extra = sizeof(HMENU),
+        .cursor = IDC_ARROW,
+        .brush = (HBRUSH)(COLOR_MENU + 1),
+    },
+    [NTUSER_WNDPROC_DESKTOP] =
+    {
+        .name = "#32769", /* DESKTOP_CLASS_ATOM */
+        .style = CS_DBLCLKS,
+        .brush = (HBRUSH)(COLOR_BACKGROUND + 1),
+    },
+    [NTUSER_WNDPROC_DEFWND] = {0},
+    [NTUSER_WNDPROC_ICONTITLE] =
+    {
+        .name = "#32772", /* ICONTITLE_CLASS_ATOM */
+        .cursor = IDC_ARROW,
+    },
+    [NTUSER_WNDPROC_UNKNOWN] = {0},
+    [NTUSER_WNDPROC_BUTTON] =
+    {
+        .name = "Button",
+        .style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC,
+        .extra = sizeof(UINT) + 2 * sizeof(HANDLE),
+        .cursor = IDC_ARROW,
+    },
+    [NTUSER_WNDPROC_COMBO] =
+    {
+        .name = "ComboBox",
+        .style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
+        .extra = sizeof(void *),
+        .cursor = IDC_ARROW,
+    },
+    [NTUSER_WNDPROC_COMBOLBOX] =
+    {
+        .name = "ComboLBox",
+        .style = CS_DBLCLKS | CS_SAVEBITS,
+        .extra = sizeof(void *),
+        .cursor = IDC_ARROW,
+    },
+    [NTUSER_WNDPROC_DIALOG] =
+    {
+        .name = "#32770", /* DIALOG_CLASS_ATOM */
+        .style = CS_SAVEBITS | CS_DBLCLKS,
+        .extra = DLGWINDOWEXTRA,
+        .cursor = IDC_ARROW,
+    },
+    [NTUSER_WNDPROC_EDIT] =
+    {
+        .name = "Edit",
+        .style = CS_DBLCLKS | CS_PARENTDC,
+        .extra = sizeof(UINT64),
+        .cursor = IDC_IBEAM,
+    },
+    [NTUSER_WNDPROC_LISTBOX] =
+    {
+        .name = "ListBox",
+        .style = CS_DBLCLKS,
+        .extra = sizeof(void *),
+        .cursor = IDC_ARROW,
+    },
+    [NTUSER_WNDPROC_MDICLIENT] =
+    {
+        .name = "MDIClient",
+        .extra = 2 * sizeof(void *),
+        .cursor = IDC_ARROW,
+        .brush = (HBRUSH)(COLOR_APPWORKSPACE + 1),
+    },
+    [NTUSER_WNDPROC_STATIC] =
+    {
+        .name = "Static",
+        .style = CS_DBLCLKS | CS_PARENTDC,
+        .extra = 2 * sizeof(HANDLE),
+        .cursor = IDC_ARROW,
+    },
+    [NTUSER_WNDPROC_IME] =
+    {
+        .name = "IME",
+        .extra = 2 * sizeof(LONG_PTR),
+        .cursor = IDC_ARROW,
+    },
+    [NTUSER_WNDPROC_GHOST] = {0},
+};
+C_ASSERT( ARRAY_SIZE(builtin_classes) == NTUSER_NB_PROCS );
+
 typedef struct tagWINDOWPROC
 {
     WNDPROC  procA;    /* ANSI window proc */
@@ -452,7 +546,7 @@ static UINT_PTR get_class_instance( CLASS *class )
     NTSTATUS status;
 
     while ((status = get_shared_class( class, &lock, &class_shm )) == STATUS_PENDING)
-        instance = class_shm->instance;
+        instance = class_shm->info.instance;
     if (status) return 0;
     return instance;
 }
@@ -485,7 +579,15 @@ static CLASS *find_class( HINSTANCE module, UNICODE_STRING *name )
  */
 WNDPROC get_class_winproc( CLASS *class )
 {
-    return class->winproc;
+    struct object_lock lock = OBJECT_LOCK_INIT;
+    const class_shm_t *class_shm;
+    WNDPROC wndproc = NULL;
+    NTSTATUS status;
+
+    while ((status = get_shared_class( class, &lock, &class_shm )) == STATUS_PENDING)
+        wndproc = wine_server_get_ptr( class_shm->info.wndproc );
+    if (status) return 0;
+    return wndproc;
 }
 
 /***********************************************************************
@@ -510,14 +612,14 @@ struct dce *set_class_dce( CLASS *class, struct dce *dce )
  *	     NtUserRegisterClassExWOW   (win32u.@)
  */
 ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *name, UNICODE_STRING *version,
-                                      struct client_menu_name *client_menu_name, DWORD fnid,
-                                      DWORD flags, DWORD *wow )
+                                      struct client_menu_name *menu_name, DWORD fnid, DWORD flags, DWORD *wow )
 {
     const BOOL is_builtin = fnid, ansi = flags;
     const shared_object_t *shared;
     struct obj_locator locator;
+    HICON icon_internal;
     HINSTANCE instance;
-    HICON sm_icon = 0;
+    WNDPROC wndproc;
     CLASS *class;
     ATOM atom;
     BOOL ret;
@@ -532,6 +634,7 @@ ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *nam
          return 0;
     }
     if (!(instance = wc->hInstance)) instance = RtlGetCurrentPeb()->ImageBaseAddress;
+    wndproc = alloc_winproc( wc->lpfnWndProc, ansi );
 
     TRACE( "name=%s hinst=%p style=0x%x clExtr=0x%x winExtr=0x%x\n",
            debugstr_us(name), instance, wc->style, wc->cbClsExtra, wc->cbWndExtra );
@@ -548,20 +651,26 @@ ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *nam
     class->local      = !is_builtin && !(wc->style & CS_GLOBALCLASS);
 
     /* Other non-null values must be set by caller */
-    if (wc->hIcon && !wc->hIconSm)
-        sm_icon = CopyImage( wc->hIcon, IMAGE_ICON,
-                             get_system_metrics( SM_CXSMICON ),
-                             get_system_metrics( SM_CYSMICON ),
-                             LR_COPYFROMRESOURCE );
+    icon_internal = wc->hIconSm ? 0 : create_small_icon( wc->hIcon );
 
     user_lock();
     SERVER_START_REQ( create_class )
     {
+        struct class_info info =
+        {
+            .style      = wc->style,
+            .cls_extra  = wc->cbClsExtra,
+            .win_extra  = wc->cbWndExtra,
+            .cursor     = wine_server_user_handle( wc->hCursor ),
+            .background = wine_server_user_handle( wc->hbrBackground ),
+            .icon       = wine_server_user_handle( wc->hIcon ),
+            .icon_small = wine_server_user_handle( wc->hIconSm ),
+            .instance   = wine_server_client_ptr( instance ),
+            .wndproc    = wine_server_client_ptr( wndproc ),
+            .menu_name  = wine_server_client_ptr( menu_name ),
+        };
+        wine_server_add_data( req, &info, sizeof(info) );
         req->local      = class->local;
-        req->style      = wc->style;
-        req->instance   = wine_server_client_ptr( instance );
-        req->cls_extra  = wc->cbClsExtra;
-        req->win_extra  = wc->cbWndExtra;
         req->client_ptr = wine_server_client_ptr( class );
         req->atom       = wine_server_add_atom( req, name );
         req->name_offset = version->Length / sizeof(WCHAR);
@@ -593,20 +702,14 @@ ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *nam
            debugstr_w(wc->lpszClassName), debugstr_us(name), atom, wc->lpfnWndProc, instance,
            wc->hbrBackground, wc->style, wc->cbClsExtra, wc->cbWndExtra, class );
 
-    class->hIcon         = wc->hIcon;
-    class->hIconSm       = wc->hIconSm;
-    class->hIconSmIntern = sm_icon;
-    class->hCursor       = wc->hCursor;
-    class->hbrBackground = wc->hbrBackground;
-    class->winproc       = alloc_winproc( wc->lpfnWndProc, ansi );
-    if (client_menu_name) class->menu_name = *client_menu_name;
+    class->icon_internal = icon_internal;
     class->shared        = shared;
     release_class_ptr( class );
     return atom;
 
 failed:
     user_unlock();
-    NtUserDestroyCursor( sm_icon, 0 );
+    NtUserDestroyCursor( icon_internal, 0 );
     free( class );
     return 0;
 
@@ -615,11 +718,11 @@ failed:
 /***********************************************************************
  *	     NtUserUnregisterClass   (win32u.@)
  */
-BOOL WINAPI NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance,
-                                   struct client_menu_name *client_menu_name )
+BOOL WINAPI NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance, struct client_menu_name **menu_name )
 {
     struct list drawables = LIST_INIT( drawables );
     CLASS *class = NULL;
+    HBRUSH background;
 
     /* create the desktop window to trigger builtin class registration */
     get_desktop_window();
@@ -630,7 +733,10 @@ BOOL WINAPI NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance,
     {
         req->instance = wine_server_client_ptr( instance );
         req->atom     = wine_server_add_atom( req, name );
-        if (!wine_server_call_err( req )) class = wine_server_get_ptr( reply->client_ptr );
+        wine_server_call_err( req );
+        class = wine_server_get_ptr( reply->client_ptr );
+        background = wine_server_ptr_handle( reply->background );
+        *menu_name = wine_server_get_ptr( reply->menu_name );
     }
     SERVER_END_REQ;
     if (!class)
@@ -643,10 +749,9 @@ BOOL WINAPI NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance,
 
     if (class->dce) free_dce( class->dce, 0, &drawables );
     list_remove( &class->entry );
-    if (class->hbrBackground > (HBRUSH)(COLOR_GRADIENTINACTIVECAPTION + 1))
-        NtGdiDeleteObjectApp( class->hbrBackground );
-    *client_menu_name = class->menu_name;
-    NtUserDestroyCursor( class->hIconSmIntern, 0 );
+    if (background > (HBRUSH)(COLOR_GRADIENTINACTIVECAPTION + 1))
+        NtGdiDeleteObjectApp( background );
+    NtUserDestroyCursor( class->icon_internal, 0 );
     free( class );
     user_unlock();
 
@@ -658,7 +763,7 @@ BOOL WINAPI NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance,
  *	     NtUserGetClassInfo   (win32u.@)
  */
 ATOM WINAPI NtUserGetClassInfoEx( HINSTANCE instance, UNICODE_STRING *name, WNDCLASSEXW *wc,
-                                  struct client_menu_name *menu_name, BOOL ansi )
+                                  struct client_menu_name **menu_name, BOOL ansi )
 {
     struct object_lock lock = OBJECT_LOCK_INIT;
     const class_shm_t *class_shm;
@@ -675,25 +780,24 @@ ATOM WINAPI NtUserGetClassInfoEx( HINSTANCE instance, UNICODE_STRING *name, WNDC
     {
         if (wc)
         {
-            wc->style         = class_shm->style;
-            wc->lpfnWndProc   = get_winproc( class->winproc, ansi );
-            wc->cbClsExtra    = class_shm->cls_extra;
-            wc->cbWndExtra    = class_shm->win_extra;
+            wc->style         = class_shm->info.style;
+            wc->lpfnWndProc   = get_winproc( wine_server_get_ptr( class_shm->info.wndproc ), ansi );
+            wc->cbClsExtra    = class_shm->info.cls_extra;
+            wc->cbWndExtra    = class_shm->info.win_extra;
             wc->hInstance     = (instance == user32_module) ? 0 : instance;
-            wc->hIcon         = class->hIcon;
-            wc->hIconSm       = class->hIconSm ? class->hIconSm : class->hIconSmIntern;
-            wc->hCursor       = class->hCursor;
-            wc->hbrBackground = class->hbrBackground;
-            wc->lpszMenuName  = ansi ? (const WCHAR *)class->menu_name.nameA : class->menu_name.nameW;
+            wc->hIcon         = wine_server_ptr_handle( class_shm->info.icon );
+            wc->hIconSm       = wine_server_ptr_handle( class_shm->info.icon_small );
+            wc->hCursor       = wine_server_ptr_handle( class_shm->info.cursor );
+            wc->hbrBackground = wine_server_ptr_handle( class_shm->info.background );
+            wc->lpszMenuName  = wine_server_get_ptr( class_shm->info.menu_name );
             wc->lpszClassName = name->Buffer;
+            *menu_name        = wine_server_get_ptr( class_shm->info.menu_name );
         }
-        atom = class_shm->atom;
+        atom = class_shm->info.atom;
     }
-    if (status) return 0;
-
-    if (menu_name) *menu_name = class->menu_name;
+    if (wc && !wc->hIconSm) wc->hIconSm = class->icon_internal;
     release_class_ptr( class );
-    return atom;
+    return status ? 0 : atom;
 }
 
 /***********************************************************************
@@ -785,6 +889,8 @@ INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name )
     WCHAR buffer[MAX_ATOM_LEN];
     NTSTATUS status;
     UINT len = 0;
+    ATOM atom;
+    WORD fnid;
     int ret;
 
     TRACE( "%p %x %p\n", hwnd, real, name );
@@ -795,6 +901,13 @@ INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name )
         return 0;
     }
 
+    if (real && (fnid = get_window_fnid( hwnd )) && (fnid & 0x7fff) < ARRAY_SIZE(builtin_classes))
+    {
+        get_desktop_window(); /* create the desktop window to trigger builtin class registration */
+        atom = builtin_classes[fnid & 0x7fff].atom;
+        return NtUserGetAtomName( atom, name );
+    }
+
     while ((status = get_shared_window_class( hwnd, &lock, &class_shm )) == STATUS_PENDING)
     {
         len = class_shm->name_len - class_shm->name_offset * sizeof(WCHAR);
@@ -828,101 +941,26 @@ static BOOL set_server_info( HWND hwnd, INT offset, LONG_PTR newval, UINT size,
 static ULONG_PTR set_class_long_size( HWND hwnd, INT offset, LONG_PTR newval, UINT size, BOOL ansi )
 {
     ULONG_PTR retval = 0;
-    HICON small_icon = 0;
     CLASS *class;
 
     if (!(class = get_class_ptr( hwnd, TRUE ))) return 0;
 
     switch(offset)
     {
-    case GCLP_MENUNAME:
-        {
-            struct client_menu_name *menu_name = (void *)newval;
-            struct client_menu_name prev = class->menu_name;
-            class->menu_name = *menu_name;
-            *menu_name = prev;
-            retval = 0;  /* Old value is now meaningless anyway */
-            break;
-        }
-    case GCLP_WNDPROC:
-        retval = (ULONG_PTR)get_winproc( class->winproc, ansi );
-        class->winproc = alloc_winproc( (WNDPROC)newval, ansi );
-        break;
+    case GCL_CBWNDEXTRA:
+    case GCL_STYLE:
     case GCLP_HBRBACKGROUND:
-        retval = (ULONG_PTR)class->hbrBackground;
-        class->hbrBackground = (HBRUSH)newval;
-        break;
     case GCLP_HCURSOR:
-        retval = (ULONG_PTR)class->hCursor;
-        class->hCursor = (HCURSOR)newval;
-        break;
     case GCLP_HICON:
-        retval = (ULONG_PTR)class->hIcon;
-        if (retval == newval) break;
-        if (newval && !class->hIconSm)
-        {
-            release_class_ptr( class );
-
-            small_icon = CopyImage( (HICON)newval, IMAGE_ICON,
-                                    get_system_metrics( SM_CXSMICON ),
-                                    get_system_metrics( SM_CYSMICON ),
-                                    LR_COPYFROMRESOURCE );
-
-            if (!(class = get_class_ptr( hwnd, TRUE )))
-            {
-                NtUserDestroyCursor( small_icon, 0 );
-                return 0;
-            }
-            if (retval != HandleToUlong( class->hIcon ) || class->hIconSm)
-            {
-                /* someone beat us, restart */
-                release_class_ptr( class );
-                NtUserDestroyCursor( small_icon, 0 );
-                return set_class_long_size( hwnd, offset, newval, size, ansi );
-            }
-        }
-        if (class->hIconSmIntern) NtUserDestroyCursor( class->hIconSmIntern, 0 );
-        class->hIcon = (HICON)newval;
-        class->hIconSmIntern = small_icon;
-        break;
     case GCLP_HICONSM:
-        retval = (ULONG_PTR)class->hIconSm;
-        if (retval == newval) break;
-        if (retval && !newval && class->hIcon)
-        {
-            HICON icon = class->hIcon;
-            release_class_ptr( class );
-
-            small_icon = CopyImage( icon, IMAGE_ICON,
-                                    get_system_metrics( SM_CXSMICON ),
-                                    get_system_metrics( SM_CYSMICON ),
-                                    LR_COPYFROMRESOURCE );
-
-            if (!(class = get_class_ptr( hwnd, TRUE )))
-            {
-                NtUserDestroyCursor( small_icon, 0 );
-                return 0;
-            }
-            if (class->hIcon != icon || !class->hIconSm)
-            {
-                /* someone beat us, restart */
-                release_class_ptr( class );
-                NtUserDestroyCursor( small_icon, 0 );
-                return set_class_long_size( hwnd, offset, newval, size, ansi );
-            }
-        }
-        if (class->hIconSmIntern) NtUserDestroyCursor( class->hIconSmIntern, 0 );
-        class->hIconSm = (HICON)newval;
-        class->hIconSmIntern = small_icon;
-        break;
-    case GCL_STYLE:
-        if (!set_server_info( hwnd, offset, newval, size, &retval )) break;
-        break;
-    case GCL_CBWNDEXTRA:
-        if (!set_server_info( hwnd, offset, newval, size, &retval )) break;
-        break;
     case GCLP_HMODULE:
+    case GCLP_MENUNAME:
+        set_server_info( hwnd, offset, newval, size, &retval );
+        break;
+    case GCLP_WNDPROC:
+        newval = (ULONG_PTR)alloc_winproc( (WNDPROC)newval, ansi );
         if (!set_server_info( hwnd, offset, newval, size, &retval )) break;
+        retval = (ULONG_PTR)get_winproc( (WNDPROC)retval, ansi );
         break;
     case GCL_CBCLSEXTRA:  /* cannot change this one */
         RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
@@ -932,6 +970,24 @@ static ULONG_PTR set_class_long_size( HWND hwnd, INT offset, LONG_PTR newval, UI
         else RtlSetLastWin32Error( ERROR_INVALID_INDEX );
         break;
     }
+
+    if (offset == GCLP_HICON || offset == GCLP_HICONSM)
+    {
+        struct object_lock lock = OBJECT_LOCK_INIT;
+        HICON icon = 0, icon_small = 0;
+        const class_shm_t *class_shm;
+        NTSTATUS status;
+
+        while ((status = get_shared_class( class, &lock, &class_shm )) == STATUS_PENDING)
+        {
+            icon_small = wine_server_get_ptr( class_shm->info.icon_small );
+            icon = wine_server_get_ptr( class_shm->info.icon );
+        }
+
+        NtUserDestroyCursor( class->icon_internal, 0 );
+        class->icon_internal = icon_small ? 0 : create_small_icon( icon );
+    }
+
     release_class_ptr( class );
     return retval;
 }
@@ -960,25 +1016,32 @@ WORD WINAPI NtUserSetClassWord( HWND hwnd, INT offset, WORD newval )
     return set_class_long_size( hwnd, offset, newval, sizeof(WORD), TRUE );
 }
 
-static ULONG_PTR get_class_long_shm( HWND hwnd, INT offset, UINT size, BOOL ansi )
+static ULONG_PTR get_class_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi )
 {
     struct object_lock lock = OBJECT_LOCK_INIT;
     const class_shm_t *class_shm;
     ULONG_PTR ret = 0;
     BOOL valid = TRUE;
     NTSTATUS status;
+    CLASS *class;
 
     while ((status = get_shared_window_class( hwnd, &lock, &class_shm )) == STATUS_PENDING)
     {
         switch (offset)
         {
-        case GCW_ATOM:           ret = class_shm->atom; break;
-        case GCL_STYLE:          ret = class_shm->style; break;
-        case GCL_CBCLSEXTRA:     ret = class_shm->cls_extra; break;
-        case GCL_CBWNDEXTRA:     ret = class_shm->win_extra; break;
-        case GCLP_HMODULE:       ret = class_shm->instance; break;
+        case GCW_ATOM:           ret = class_shm->info.atom; break;
+        case GCL_STYLE:          ret = class_shm->info.style; break;
+        case GCLP_WNDPROC:       ret = class_shm->info.wndproc; break;
+        case GCL_CBCLSEXTRA:     ret = class_shm->info.cls_extra; break;
+        case GCL_CBWNDEXTRA:     ret = class_shm->info.win_extra; break;
+        case GCLP_HMODULE:       ret = class_shm->info.instance; break;
+        case GCLP_HICON:         ret = class_shm->info.icon; break;
+        case GCLP_HICONSM:       ret = class_shm->info.icon_small; break;
+        case GCLP_HCURSOR:       ret = class_shm->info.cursor; break;
+        case GCLP_HBRBACKGROUND: ret = class_shm->info.background; break;
+        case GCLP_MENUNAME:      ret = (ULONG_PTR)wine_server_get_ptr( class_shm->info.menu_name ); break;
         default:
-            valid = offset >= 0 && offset <= (INT)(class_shm->cls_extra - size);
+            valid = offset >= 0 && offset <= (INT)(class_shm->info.cls_extra - size);
             if (valid) memcpy( &ret, (char *)class_shm->extra + offset, size );
             break;
         }
@@ -995,84 +1058,13 @@ static ULONG_PTR get_class_long_shm( HWND hwnd, INT offset, UINT size, BOOL ansi
         return 0;
     }
 
-    return ret;
-}
-
-static ULONG_PTR get_class_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi )
-{
-    CLASS *class;
-    ULONG_PTR retvalue = 0;
-
-    switch (offset)
-    {
-    case GCLP_HICONSM:
-    case GCLP_WNDPROC:
-    case GCLP_HICON:
-    case GCLP_HCURSOR:
-    case GCLP_HBRBACKGROUND:
-    case GCLP_MENUNAME:
-        break;
-    default:
-        return get_class_long_shm( hwnd, offset, size, ansi );
-    }
-
-    if (!(class = get_class_ptr( hwnd, FALSE ))) return 0;
-
-    if (class == OBJ_OTHER_PROCESS)
-    {
-        SERVER_START_REQ( get_class_info )
-        {
-            req->window = wine_server_user_handle( hwnd );
-            req->offset = offset;
-            req->size = size;
-            if (!wine_server_call_err( req ))
-            {
-                switch (offset)
-                {
-                case GCLP_HBRBACKGROUND:
-                case GCLP_HCURSOR:
-                case GCLP_HICON:
-                case GCLP_HICONSM:
-                case GCLP_WNDPROC:
-                case GCLP_MENUNAME:
-                    FIXME( "offset %d not supported on other process window %p\n", offset, hwnd );
-                    break;
-                default:
-                    retvalue = reply->info;
-                    break;
-                }
-            }
-        }
-        SERVER_END_REQ;
-        return retvalue;
-    }
-
-    switch(offset)
+    if (offset == GCLP_WNDPROC) return (ULONG_PTR)get_winproc( (WNDPROC)ret, ansi );
+    if (offset == GCLP_HICONSM && !ret && (class = get_class_ptr( hwnd, FALSE )))
     {
-    case GCLP_HBRBACKGROUND:
-        retvalue = (ULONG_PTR)class->hbrBackground;
-        break;
-    case GCLP_HCURSOR:
-        retvalue = (ULONG_PTR)class->hCursor;
-        break;
-    case GCLP_HICON:
-        retvalue = (ULONG_PTR)class->hIcon;
-        break;
-    case GCLP_HICONSM:
-        retvalue = (ULONG_PTR)(class->hIconSm ? class->hIconSm : class->hIconSmIntern);
-        break;
-    case GCLP_WNDPROC:
-        retvalue = (ULONG_PTR)get_winproc( class->winproc, ansi );
-        break;
-    case GCLP_MENUNAME:
-        retvalue = ansi ? (ULONG_PTR)class->menu_name.nameA : (ULONG_PTR)class->menu_name.nameW;
-        break;
-    default:
-        RtlSetLastWin32Error( ERROR_INVALID_INDEX );
-        break;
+        ret = (UINT_PTR)class->icon_internal;
+        release_class_ptr( class );
     }
-    release_class_ptr( class );
-    return retvalue;
+    return ret;
 }
 
 DWORD get_class_long( HWND hwnd, INT offset, BOOL ansi )
@@ -1090,109 +1082,11 @@ WORD get_class_word( HWND hwnd, INT offset )
     return get_class_long_size( hwnd, offset, sizeof(WORD), TRUE );
 }
 
-static const struct builtin_class_descr desktop_builtin_class =
-{
-    .name = "#32769", /* DESKTOP_CLASS_ATOM */
-    .style = CS_DBLCLKS,
-    .proc = NTUSER_WNDPROC_DESKTOP,
-    .brush = (HBRUSH)(COLOR_BACKGROUND + 1),
-};
-
-static const struct builtin_class_descr message_builtin_class =
-{
-    .name = "Message",
-    .proc = NTUSER_WNDPROC_MESSAGE,
-};
-
-static const struct builtin_class_descr builtin_classes[] =
+ATOM get_builtin_class_atom( enum ntuser_client_procs proc )
 {
-    /* button */
-    {
-        .name = "Button",
-        .style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC,
-        .proc = NTUSER_WNDPROC_BUTTON,
-        .extra = sizeof(UINT) + 2 * sizeof(HANDLE),
-        .cursor = IDC_ARROW,
-    },
-    /* combo  */
-    {
-        .name = "ComboBox",
-        .style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
-        .proc = NTUSER_WNDPROC_COMBO,
-        .extra = sizeof(void *),
-        .cursor = IDC_ARROW,
-    },
-    /* combolbox */
-    {
-        .name = "ComboLBox",
-        .style = CS_DBLCLKS | CS_SAVEBITS,
-        .proc = NTUSER_WNDPROC_COMBOLBOX,
-        .extra = sizeof(void *),
-        .cursor = IDC_ARROW,
-    },
-    /* dialog */
-    {
-        .name = "#32770", /* DIALOG_CLASS_ATOM */
-        .style = CS_SAVEBITS | CS_DBLCLKS,
-        .proc = NTUSER_WNDPROC_DIALOG,
-        .extra = DLGWINDOWEXTRA,
-        .cursor = IDC_ARROW,
-    },
-    /* icon title */
-    {
-        .name = "#32772", /* ICONTITLE_CLASS_ATOM */
-        .proc = NTUSER_WNDPROC_ICONTITLE,
-        .cursor = IDC_ARROW,
-    },
-    /* IME */
-    {
-        .name = "IME",
-        .proc = NTUSER_WNDPROC_IME,
-        .extra = 2 * sizeof(LONG_PTR),
-        .cursor = IDC_ARROW,
-    },
-    /* listbox  */
-    {
-        .name = "ListBox",
-        .style = CS_DBLCLKS,
-        .proc = NTUSER_WNDPROC_LISTBOX,
-        .extra = sizeof(void *),
-        .cursor = IDC_ARROW,
-    },
-    /* menu */
-    {
-        .name = "#32768", /* POPUPMENU_CLASS_ATOM */
-        .style = CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS,
-        .proc = NTUSER_WNDPROC_MENU,
-        .extra = sizeof(HMENU),
-        .cursor = IDC_ARROW,
-        .brush = (HBRUSH)(COLOR_MENU + 1),
-    },
-    /* MDIClient */
-    {
-        .name = "MDIClient",
-        .proc = NTUSER_WNDPROC_MDICLIENT,
-        .extra = 2 * sizeof(void *),
-        .cursor = IDC_ARROW,
-        .brush = (HBRUSH)(COLOR_APPWORKSPACE + 1),
-    },
-    /* scrollbar */
-    {
-        .name = "ScrollBar",
-        .style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC,
-        .proc = NTUSER_WNDPROC_SCROLLBAR,
-        .extra = sizeof(struct scroll_bar_win_data),
-        .cursor = IDC_ARROW,
-    },
-    /* static */
-    {
-        .name = "Static",
-        .style = CS_DBLCLKS | CS_PARENTDC,
-        .proc = NTUSER_WNDPROC_STATIC,
-        .extra = 2 * sizeof(HANDLE),
-        .cursor = IDC_ARROW,
-    },
-};
+    get_desktop_window(); /* create the desktop window to trigger builtin class registration */
+    return builtin_classes[proc].atom;
+}
 
 /***********************************************************************
  *           register_builtin
@@ -1200,18 +1094,19 @@ static const struct builtin_class_descr builtin_classes[] =
  * Register a builtin control class.
  * This allows having both ANSI and Unicode winprocs for the same class.
  */
-static void register_builtin( const struct builtin_class_descr *descr )
+static void register_builtin( enum ntuser_client_procs proc )
 {
+    struct builtin_class_descr *descr = builtin_classes + proc;
     UNICODE_STRING name, version = { .Length = 0 };
-    struct client_menu_name menu_name = { 0 };
     WCHAR nameW[64];
-    WNDCLASSEXW class = {
+    WNDCLASSEXW class =
+    {
         .cbSize = sizeof(class),
         .hInstance = user32_module,
         .style = descr->style,
         .cbWndExtra = descr->extra,
         .hbrBackground = descr->brush,
-        .lpfnWndProc = BUILTIN_WINPROC( descr->proc ),
+        .lpfnWndProc = BUILTIN_WINPROC( proc ),
     };
 
     if (descr->cursor)
@@ -1221,28 +1116,21 @@ static void register_builtin( const struct builtin_class_descr *descr )
     asciiz_to_unicode( nameW, descr->name );
     RtlInitUnicodeString( &name, nameW );
 
-    if (!NtUserRegisterClassExWOW( &class, &name, &version, &menu_name, 1, 0, NULL ) && class.hCursor)
-        NtUserDestroyCursor( class.hCursor, 0 );
+    if ((descr->atom = NtUserRegisterClassExWOW( &class, &name, &version, NULL, 1, 0, NULL ))) return;
+    if (class.hCursor) NtUserDestroyCursor( class.hCursor, 0 );
 }
 
 static void register_builtins(void)
 {
+    struct builtin_class_descr *edit_class = builtin_classes + NTUSER_WNDPROC_EDIT;
     ULONG ret_len, i;
     void *ret_ptr;
 
     /* 64-bit Windows use sizeof(UINT64) for all processes, while 32-bit Windows use 6 for extra
      * bytes size. Civilization II depends on the size being 6, so we use that even in wow64. */
-    const struct builtin_class_descr edit_class =
-    {
-        .name = "Edit",
-        .style = CS_DBLCLKS | CS_PARENTDC,
-        .proc = NTUSER_WNDPROC_EDIT,
-        .extra = sizeof(void *) == 4 || NtCurrentTeb()->WowTebOffset ? 6 : sizeof(UINT64),
-        .cursor = IDC_IBEAM,
-    };
+    if (sizeof(void *) == 4 || NtCurrentTeb()->WowTebOffset) edit_class->extra = 6;
 
-    for (i = 0; i < ARRAYSIZE(builtin_classes); i++) register_builtin( &builtin_classes[i] );
-    register_builtin( &edit_class );
+    for (i = 0; i < ARRAY_SIZE(builtin_classes); i++) if (builtin_classes[i].name) register_builtin( i );
     KeUserModeCallback( NtUserInitBuiltinClasses, NULL, 0, &ret_ptr, &ret_len );
 }
 
@@ -1260,6 +1148,6 @@ void register_builtin_classes(void)
  */
 void register_desktop_class(void)
 {
-    register_builtin( &desktop_builtin_class );
-    register_builtin( &message_builtin_class );
+    register_builtin( NTUSER_WNDPROC_DESKTOP );
+    register_builtin( NTUSER_WNDPROC_MESSAGE );
 }
diff --git a/dlls/win32u/cursoricon.c b/dlls/win32u/cursoricon.c
index 886540067bee..b7bb81124762 100644
--- a/dlls/win32u/cursoricon.c
+++ b/dlls/win32u/cursoricon.c
@@ -448,7 +448,7 @@ HCURSOR WINAPI NtUserGetCursorFrameInfo( HCURSOR cursor, DWORD istep, DWORD *rat
  *
  * Helper function to duplicate a bitmap.
  */
-static HBITMAP copy_bitmap( HBITMAP bitmap )
+static HBITMAP copy_bitmap( HBITMAP bitmap, const SIZE *size )
 {
     HDC src, dst = 0;
     HBITMAP new_bitmap = 0;
@@ -460,10 +460,11 @@ static HBITMAP copy_bitmap( HBITMAP bitmap )
     if ((src = NtGdiCreateCompatibleDC( 0 )) && (dst = NtGdiCreateCompatibleDC( 0 )))
     {
         NtGdiSelectBitmap( src, bitmap );
-        if ((new_bitmap = NtGdiCreateCompatibleBitmap( src, bmp.bmWidth, bmp.bmHeight )))
+        if ((new_bitmap = NtGdiCreateCompatibleBitmap( src, size ? size->cx : bmp.bmWidth, size ? size->cy : bmp.bmHeight )))
         {
             NtGdiSelectBitmap( dst, new_bitmap );
-            NtGdiBitBlt( dst, 0, 0, bmp.bmWidth, bmp.bmHeight, src, 0, 0, SRCCOPY, 0, 0 );
+            if (size) NtGdiStretchBlt( dst, 0, 0, size->cx, size->cy, src, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY, 0 );
+            else NtGdiBitBlt( dst, 0, 0, bmp.bmWidth, bmp.bmHeight, src, 0, 0, SRCCOPY, 0, 0 );
         }
     }
     NtGdiDeleteObjectApp( dst );
@@ -496,8 +497,8 @@ BOOL WINAPI NtUserGetIconInfo( HICON icon, ICONINFO *info, UNICODE_STRING *modul
     info->fIcon        = obj->is_icon;
     info->xHotspot     = frame_obj->frame.hotspot.x;
     info->yHotspot     = frame_obj->frame.hotspot.y;
-    info->hbmColor     = copy_bitmap( frame_obj->frame.color );
-    info->hbmMask      = copy_bitmap( frame_obj->frame.mask );
+    info->hbmColor     = copy_bitmap( frame_obj->frame.color, NULL );
+    info->hbmMask      = copy_bitmap( frame_obj->frame.mask, NULL );
     if (!info->hbmMask || (!info->hbmColor && frame_obj->frame.color))
     {
         NtGdiDeleteObjectApp( info->hbmMask );
@@ -710,6 +711,40 @@ ULONG_PTR set_icon_param( HICON handle, const struct free_icon_params *params )
     return ret;
 }
 
+HICON create_small_icon( HICON handle )
+{
+    struct cursoricon_frame frame = {0};
+    struct cursoricon_desc desc = {0};
+    struct cursoricon_object *obj;
+    HICON icon;
+    SIZE size;
+
+    if (!handle) return 0;
+
+    desc.frames = &frame;
+    frame.width = size.cx = get_system_metrics( SM_CXSMICON );
+    frame.height = size.cy = get_system_metrics( SM_CYSMICON );
+    frame.hotspot.x = frame.width / 2;
+    frame.hotspot.y = frame.height / 2;
+
+    if (!(obj = get_user_handle_ptr( handle, NTUSER_OBJ_ICON )) || obj == OBJ_OTHER_PROCESS) return 0;
+    frame.color = copy_bitmap( obj->frame.color, &size );
+    frame.alpha = copy_bitmap( obj->frame.alpha, &size );
+    frame.mask = copy_bitmap( obj->frame.mask, &size );
+    release_user_handle_ptr( obj );
+
+    if (!(icon = alloc_cursoricon_handle( TRUE )) || !NtUserSetCursorIconData( icon, NULL, NULL, &desc ))
+    {
+        NtGdiDeleteObjectApp( frame.color );
+        NtGdiDeleteObjectApp( frame.alpha );
+        NtGdiDeleteObjectApp( frame.mask );
+        NtUserDestroyCursor( icon, 0 );
+        return 0;
+    }
+
+    return icon;
+}
+
 /******************************************************************************
  *	     CopyImage (win32u.so)
  */
diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c
index dc3611a06ab7..5951374fc9a8 100644
--- a/dlls/win32u/defwnd.c
+++ b/dlls/win32u/defwnd.c
@@ -674,7 +674,7 @@ static BOOL on_bottom_border( int hit )
  */
 static void sys_command_size_move( HWND hwnd, WPARAM wparam )
 {
-    DWORD msg_pos = NtUserGetThreadInfo()->message_pos;
+    DWORD msg_pos = NtUserGetMessagePos();
     BOOL thickframe, drag_full_windows = TRUE, moved = FALSE;
     RECT sizing_rect, mouse_rect, orig_rect;
     UINT hittest = wparam & 0x0f;
@@ -800,7 +800,7 @@ static void sys_command_size_move( HWND hwnd, WPARAM wparam )
              * WM_LBUTTONUP. Detect that and terminate the loop as if we'd gotten it. */
             if (!(NtUserGetKeyState( VK_LBUTTON ) & 0x8000))
             {
-                DWORD last_pos = NtUserGetThreadInfo()->message_pos;
+                DWORD last_pos = NtUserGetMessagePos();
                 pt.x = ((int)(short)LOWORD( last_pos ));
                 pt.y = ((int)(short)HIWORD( last_pos ));
                 break;
@@ -959,6 +959,7 @@ static void track_nc_scroll_bar( HWND hwnd, WPARAM wparam, POINT pt )
 
 static LRESULT handle_sys_command( HWND hwnd, WPARAM wparam, LPARAM lparam )
 {
+    DWORD msgpos;
     POINT pos;
     RECT rect;
 
@@ -969,8 +970,9 @@ static LRESULT handle_sys_command( HWND hwnd, WPARAM wparam, LPARAM lparam )
     if (call_hooks( WH_CBT, HCBT_SYSCOMMAND, wparam, lparam, 0 ))
         return 0;
 
-    pos.x = (short)LOWORD( NtUserGetThreadInfo()->message_pos );
-    pos.y = (short)HIWORD( NtUserGetThreadInfo()->message_pos );
+    msgpos = NtUserGetMessagePos();
+    pos.x = (short)LOWORD( msgpos );
+    pos.y = (short)HIWORD( msgpos );
     NtUserLogicalToPerMonitorDPIPhysicalPoint( hwnd, &pos );
     SetRect( &rect, pos.x, pos.y, pos.x, pos.y );
     rect = map_rect_virt_to_raw( rect, 0 );
diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c
index 4d9708f377c6..c7a78cd327d1 100644
--- a/dlls/win32u/driver.c
+++ b/dlls/win32u/driver.c
@@ -765,7 +765,7 @@ static BOOL nodrv_CreateWindow( HWND hwnd )
     HWND parent = NtUserGetAncestor( hwnd, GA_PARENT );
 
     /* HWND_MESSAGE windows don't need a graphics driver */
-    if (!parent || parent == UlongToHandle( NtUserGetThreadInfo()->msg_window )) return TRUE;
+    if (!parent || parent == get_user_thread_info()->msg_window) return TRUE;
     if (warned++) return FALSE;
 
     ERR_(winediag)( "Application tried to create a window, but no driver could be loaded.\n" );
diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c
index 1c1cad94b3f1..03c454d73779 100644
--- a/dlls/win32u/imm.c
+++ b/dlls/win32u/imm.c
@@ -225,10 +225,9 @@ UINT WINAPI NtUserAssociateInputContext( HWND hwnd, HIMC ctx, ULONG flags )
 
 HIMC get_default_input_context(void)
 {
-    struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
-    if (!thread_info->default_imc)
-        thread_info->default_imc = HandleToUlong( NtUserCreateInputContext( 0 ));
-    return UlongToHandle( thread_info->default_imc );
+    struct user_thread_info *thread_info = get_user_thread_info();
+    if (!thread_info->default_imc) thread_info->default_imc = NtUserCreateInputContext( 0 );
+    return thread_info->default_imc ;
 }
 
 HIMC get_window_input_context( HWND hwnd )
@@ -415,7 +414,7 @@ void cleanup_imm_thread(void)
         thread_info->imm_thread_data = NULL;
     }
 
-    NtUserDestroyInputContext( UlongToHandle( thread_info->client_info.default_imc ));
+    NtUserDestroyInputContext( thread_info->default_imc );
 }
 
 /*****************************************************************************
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c
index a8055a9bb5dc..733eb604fdcc 100644
--- a/dlls/win32u/input.c
+++ b/dlls/win32u/input.c
@@ -871,8 +871,16 @@ DWORD WINAPI NtUserGetQueueStatus( UINT flags )
     return ret;
 }
 
+/***********************************************************************
+ *           NtUserGetMessagePos (win32u.@)
+ */
+DWORD WINAPI NtUserGetMessagePos(void)
+{
+    return get_user_thread_info()->message_pos;
+}
+
 /*******************************************************************
- *           NtUserGetThreadInfo (win32u.@)
+ *           NtUserGetThreadState (win32u.@)
  */
 ULONG_PTR WINAPI NtUserGetThreadState( USERTHREADSTATECLASS cls )
 {
@@ -899,7 +907,7 @@ ULONG_PTR WINAPI NtUserGetThreadState( USERTHREADSTATECLASS cls )
         return (ULONG_PTR)get_default_ime_window( 0 );
 
     case UserThreadStateDefaultInputContext:
-        return NtUserGetThreadInfo()->default_imc;
+        return (ULONG_PTR)get_default_input_context();
 
     case UserThreadStateInputState:
         return get_input_state();
@@ -908,13 +916,13 @@ ULONG_PTR WINAPI NtUserGetThreadState( USERTHREADSTATECLASS cls )
         return (ULONG_PTR)NtUserGetCursor();
 
     case UserThreadStateExtraInfo:
-        return NtUserGetThreadInfo()->message_extra;
+        return get_user_thread_info()->message_extra;
 
     case UserThreadStateInSendMessage:
-        return NtUserGetThreadInfo()->receive_flags;
+        return get_send_message_flags();
 
     case UserThreadStateMessageTime:
-        return NtUserGetThreadInfo()->message_time;
+        return get_user_thread_info()->message_time;
 
     case UserThreadStateIsForeground:
     default:
@@ -923,6 +931,17 @@ ULONG_PTR WINAPI NtUserGetThreadState( USERTHREADSTATECLASS cls )
     }
 }
 
+/***********************************************************************
+ *           NtUserSetMessageExtraInfo (win32u.@)
+ */
+LPARAM WINAPI NtUserSetMessageExtraInfo( LPARAM lparam )
+{
+    struct user_thread_info *thread_info = get_user_thread_info();
+    LPARAM old_value = thread_info->message_extra;
+    thread_info->message_extra = lparam;
+    return old_value;
+}
+
 /***********************************************************************
  *           get_input_state
  */
@@ -977,9 +996,6 @@ static HKL get_locale_kbd_layout(void)
 
 /***********************************************************************
  *	     NtUserGetKeyboardLayout    (win32u.@)
- *
- * Device handle for keyboard layout defaulted to
- * the language id. This is the way Windows default works.
  */
 HKL WINAPI NtUserGetKeyboardLayout( DWORD thread_id )
 {
@@ -1457,11 +1473,10 @@ BOOL WINAPI NtUserGetKeyboardLayoutName( WCHAR *name )
 
     layout = NtUserGetKeyboardLayout( 0 );
     id = HandleToUlong( layout );
-    if (HIWORD( id ) == LOWORD( id )) id = LOWORD( id );
+    if (!(HIWORD( id ) & 0xf000)) id = HIWORD( id );
     snprintf( buffer, sizeof(buffer), "%08X", id );
     asciiz_to_unicode( name, buffer );
-
-    if ((hkey = reg_open_key( NULL, keyboard_layouts_keyW, sizeof(keyboard_layouts_keyW) )))
+    if ((HIWORD( id ) & 0x1000) && (hkey = reg_open_key( NULL, keyboard_layouts_keyW, sizeof(keyboard_layouts_keyW) )))
     {
         while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key,
                                 sizeof(buffer) - sizeof(WCHAR), &len ))
diff --git a/dlls/win32u/main.c b/dlls/win32u/main.c
index 14cecf81497f..edb1d98b3f0a 100644
--- a/dlls/win32u/main.c
+++ b/dlls/win32u/main.c
@@ -1552,7 +1552,7 @@ BOOL SYSCALL_API NtUserGetCaretPos( POINT *pt )
 }
 
 ATOM SYSCALL_API NtUserGetClassInfoEx( HINSTANCE instance, UNICODE_STRING *name, WNDCLASSEXW *wc,
-                                       struct client_menu_name *menu_name, BOOL ansi )
+                                       struct client_menu_name **menu_name, BOOL ansi )
 {
     SYSCALL_FUNC( NtUserGetClassInfoEx );
 }
@@ -1720,6 +1720,11 @@ BOOL SYSCALL_API NtUserGetMessage( MSG *msg, HWND hwnd, UINT first, UINT last )
     SYSCALL_FUNC( NtUserGetMessage );
 }
 
+DWORD SYSCALL_API NtUserGetMessagePos(void)
+{
+    SYSCALL_FUNC( NtUserGetMessagePos );
+}
+
 int SYSCALL_API NtUserGetMouseMovePointsEx( UINT size, MOUSEMOVEPOINT *ptin, MOUSEMOVEPOINT *ptout,
                                             int count, DWORD resolution )
 {
@@ -2075,8 +2080,7 @@ BOOL SYSCALL_API NtUserRedrawWindow( HWND hwnd, const RECT *rect, HRGN hrgn, UIN
 }
 
 ATOM SYSCALL_API NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *name, UNICODE_STRING *version,
-                                           struct client_menu_name *client_menu_name, DWORD fnid,
-                                           DWORD flags, DWORD *wow )
+                                           struct client_menu_name *menu_name, DWORD fnid, DWORD flags, DWORD *wow )
 {
     SYSCALL_FUNC( NtUserRegisterClassExWOW );
 }
@@ -2265,6 +2269,11 @@ BOOL SYSCALL_API NtUserSetMenuDefaultItem( HMENU handle, UINT item, UINT bypos )
     SYSCALL_FUNC( NtUserSetMenuDefaultItem );
 }
 
+LPARAM SYSCALL_API NtUserSetMessageExtraInfo( LPARAM lp )
+{
+    SYSCALL_FUNC( NtUserSetMessageExtraInfo );
+}
+
 BOOL SYSCALL_API NtUserSetObjectInformation( HANDLE handle, INT index, void *info, DWORD len )
 {
     SYSCALL_FUNC( NtUserSetObjectInformation );
@@ -2491,8 +2500,7 @@ BOOL SYSCALL_API NtUserUnhookWindowsHookEx( HHOOK handle )
     SYSCALL_FUNC( NtUserUnhookWindowsHookEx );
 }
 
-BOOL SYSCALL_API NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance,
-                                        struct client_menu_name *client_menu_name )
+BOOL SYSCALL_API NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance, struct client_menu_name **menu_name )
 {
     SYSCALL_FUNC( NtUserUnregisterClass );
 }
diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c
index a0eb097896b3..1428ff586f4f 100644
--- a/dlls/win32u/message.c
+++ b/dlls/win32u/message.c
@@ -461,16 +461,16 @@ static BOOL init_window_call_params( struct win_proc_params *params, HWND hwnd,
 static LRESULT dispatch_win_proc_params( struct win_proc_params *params, size_t size,
                                          void **client_ret, size_t *client_ret_size )
 {
-    struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
+    struct user_thread_info *thread_info = get_user_thread_info();
     LRESULT result = 0;
     void *ret_ptr;
     ULONG ret_len;
     NTSTATUS status;
 
-    if (thread_info->recursion_count > MAX_WINPROC_RECURSION) return 0;
-    thread_info->recursion_count++;
+    if (thread_info->msg_call_depth > MAX_WINPROC_RECURSION) return 0;
+    thread_info->msg_call_depth++;
     status = KeUserModeCallback( NtUserCallWinProc, params, size, &ret_ptr, &ret_len );
-    thread_info->recursion_count--;
+    thread_info->msg_call_depth--;
 
     if (status) return result;
 
@@ -2093,6 +2093,15 @@ static void copy_user_result( void *buffer, size_t size, LRESULT result, UINT me
     if (copy_size) memcpy( lparam_ptr, buffer, copy_size );
 }
 
+/***********************************************************************
+ *           get_send_message_flags
+ */
+UINT get_send_message_flags(void)
+{
+    struct user_thread_info *thread_info = get_user_thread_info();
+    return thread_info->receive_info ? thread_info->receive_info->flags : ISMEX_NOSEND;
+}
+
 /***********************************************************************
  *           reply_message
  *
@@ -2109,8 +2118,6 @@ static void reply_message( struct received_message_info *info, LRESULT result, M
 
     memset( &data, 0, sizeof(data) );
     info->flags |= ISMEX_REPLIED;
-    if (info == get_user_thread_info()->receive_info)
-        NtUserGetThreadInfo()->receive_flags = info->flags;
 
     if (info->type == MSG_OTHER_PROCESS && !replied)
     {
@@ -2163,7 +2170,6 @@ static BOOL reply_winproc_result( LRESULT result, HWND hwnd, UINT message, WPARA
     reply_message( info, result, &msg );
 
     thread_info->receive_info = info->prev;
-    thread_info->client_info.receive_flags = info->prev ? info->prev->flags : ISMEX_NOSEND;
     return TRUE;
 }
 
@@ -2330,6 +2336,8 @@ static LRESULT call_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar
                                  enum message_type type, BOOL same_thread,
                                  enum wm_char_mapping mapping, BOOL ansi_dst )
 {
+    struct user_thread_info *thread_info = get_user_thread_info();
+    INPUT_MESSAGE_SOURCE prev_source;
     struct win_proc_params p, *params = &p;
     BOOL ansi = ansi_dst && type == MSG_ASCII;
     size_t packed_size = 0, offset = sizeof(*params), reply_size;
@@ -2344,6 +2352,10 @@ static LRESULT call_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar
         return handle_internal_message( hwnd, msg, wparam, lparam );
 
     if (!is_current_thread_window( hwnd )) return 0;
+    if (!thread_info->client_info) return 0;
+
+    prev_source = thread_info->client_info->msg_source;
+    thread_info->client_info->msg_source = msg_source_unavailable;
 
     packed_size = user_message_size( hwnd, msg, wparam, lparam, type == MSG_OTHER_PROCESS, ansi, &reply_size );
 
@@ -2358,13 +2370,13 @@ static LRESULT call_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar
     if (packed_size)
     {
         offset = (offset + 15) & ~15;
-        if (!(params = malloc( offset + packed_size ))) return 0;
+        if (!(params = malloc( offset + packed_size ))) goto done;
     }
 
     if (!init_window_call_params( params, hwnd, msg, wparam, lparam, ansi_dst, mapping ))
     {
         if (params != &p) free( params );
-        return 0;
+        goto done;
     }
 
     if (type == MSG_OTHER_PROCESS) params->ansi = FALSE;
@@ -2386,6 +2398,8 @@ static LRESULT call_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar
     cwpret.hwnd    = hwnd;
     call_message_hooks( WH_CALLWNDPROCRET, HC_ACTION, same_thread, (LPARAM)&cwpret, sizeof(cwpret),
                         packed_size, ansi );
+ done:
+    thread_info->client_info->msg_source = prev_source;
     return result;
 }
 
@@ -2854,13 +2868,16 @@ static BOOL process_mouse_message( MSG *msg, UINT hw_id, ULONG_PTR extra_info, H
 static BOOL process_hardware_message( MSG *msg, UINT hw_id, const struct hardware_msg_data *msg_data,
                                       HWND hwnd_filter, UINT first, UINT last, BOOL remove )
 {
-    struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
+    struct user_thread_info *thread_info = get_user_thread_info();
+    INPUT_MESSAGE_SOURCE prev_source;
     RECT rect = {msg->pt.x, msg->pt.y, msg->pt.x, msg->pt.y};
     UINT context;
     BOOL ret = FALSE;
 
-    thread_info->msg_source.deviceType = msg_data->source.device;
-    thread_info->msg_source.originId   = msg_data->source.origin;
+    if (!thread_info->client_info) return FALSE;
+    prev_source = thread_info->client_info->msg_source;
+    thread_info->client_info->msg_source.deviceType = msg_data->source.device;
+    thread_info->client_info->msg_source.originId   = msg_data->source.origin;
 
     /* hardware messages are always in raw physical coords */
     context = set_thread_dpi_awareness_context( NTUSER_DPI_PER_MONITOR_AWARE );
@@ -2883,6 +2900,7 @@ static BOOL process_hardware_message( MSG *msg, UINT hw_id, const struct hardwar
     else
         ERR( "unknown message type %x\n", msg->message );
     set_thread_dpi_awareness_context( context );
+    if (!ret) thread_info->client_info->msg_source = prev_source;
     return ret;
 }
 
@@ -2934,7 +2952,6 @@ static int peek_message( MSG *msg, const struct peek_message_filter *filter )
     HWND hwnd = filter->hwnd;
     UINT first = filter->first, last = filter->last, flags = filter->flags;
     struct user_thread_info *thread_info = get_user_thread_info();
-    INPUT_MESSAGE_SOURCE prev_source = thread_info->client_info.msg_source;
     HANDLE idle_event = thread_info->idle_event;
     struct received_message_info info;
     unsigned int hw_id = 0;  /* id of previous hardware message */
@@ -2966,7 +2983,6 @@ static int peek_message( MSG *msg, const struct peek_message_filter *filter )
         /* if filter includes QS_RAWINPUT we have to translate hardware messages */
         if (signal_bits & QS_RAWINPUT) signal_bits |= QS_KEY | QS_MOUSEMOVE | QS_MOUSEBUTTON;
 
-        thread_info->client_info.msg_source = prev_source;
         wake_mask = filter->mask & (QS_SENDMESSAGE | QS_SMRESULT);
 
         if (check_queue_bits( wake_mask, filter->mask, wake_mask | signal_bits, filter->mask | clear_bits,
@@ -3144,9 +3160,9 @@ static int peek_message( MSG *msg, const struct peek_message_filter *filter )
                     continue;  /* ignore it */
                 }
                 *msg = info.msg;
-                thread_info->client_info.message_pos   = MAKELONG( info.msg.pt.x, info.msg.pt.y );
-                thread_info->client_info.message_time  = info.msg.time;
-                thread_info->client_info.message_extra = msg_data->hardware.info;
+                thread_info->message_pos   = MAKELONG( info.msg.pt.x, info.msg.pt.y );
+                thread_info->message_time  = info.msg.time;
+                thread_info->message_extra = msg_data->hardware.info;
                 if (buffer != buffer_init) free( buffer );
                 call_hooks( WH_GETMESSAGE, HC_ACTION, flags & PM_REMOVE, (LPARAM)msg, sizeof(*msg) );
                 return 1;
@@ -3211,11 +3227,12 @@ static int peek_message( MSG *msg, const struct peek_message_filter *filter )
             }
             *msg = info.msg;
             msg->pt = point_phys_to_win_dpi( info.msg.hwnd, info.msg.pt );
-            thread_info->client_info.message_pos   = MAKELONG( msg->pt.x, msg->pt.y );
-            thread_info->client_info.message_time  = info.msg.time;
-            thread_info->client_info.message_extra = 0;
-            thread_info->client_info.msg_source = msg_source_unavailable;
+            thread_info->message_pos   = MAKELONG( msg->pt.x, msg->pt.y );
+            thread_info->message_time  = info.msg.time;
+            thread_info->message_extra = 0;
             if (buffer != buffer_init) free( buffer );
+            if (!thread_info->client_info) return 1;
+            thread_info->client_info->msg_source = msg_source_unavailable;
             call_hooks( WH_GETMESSAGE, HC_ACTION, flags & PM_REMOVE, (LPARAM)msg, sizeof(*msg) );
             return 1;
         }
@@ -3223,8 +3240,6 @@ static int peek_message( MSG *msg, const struct peek_message_filter *filter )
         /* if we get here, we have a sent message; call the window procedure */
         info.prev = thread_info->receive_info;
         thread_info->receive_info = &info;
-        thread_info->client_info.msg_source = msg_source_unavailable;
-        thread_info->client_info.receive_flags = info.flags;
         result = call_window_proc( info.msg.hwnd, info.msg.message, info.msg.wParam,
                                    info.msg.lParam, info.type, FALSE, WMCHAR_MAP_RECVMESSAGE,
                                    info.type == MSG_ASCII );
@@ -4380,8 +4395,7 @@ static LRESULT call_messageAtoW( winproc_callback_t callback, HWND hwnd, UINT ms
  */
 static BOOL process_message( struct send_message_info *info, DWORD_PTR *res_ptr, BOOL ansi )
 {
-    struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
-    INPUT_MESSAGE_SOURCE prev_source = thread_info->msg_source;
+    struct user_thread_info *thread_info = get_user_thread_info();
     DWORD dest_pid;
     BOOL ret;
     LRESULT result = 0;
@@ -4393,7 +4407,7 @@ static BOOL process_message( struct send_message_info *info, DWORD_PTR *res_ptr,
 
     if (info->params && info->dest_tid == GetCurrentThreadId() &&
         !is_hooked( WH_CALLWNDPROC ) && !is_hooked( WH_CALLWNDPROCRET ) &&
-        thread_info->recursion_count <= MAX_WINPROC_RECURSION)
+        thread_info->msg_call_depth <= MAX_WINPROC_RECURSION)
     {
         /* if we're called from client side and need just a simple winproc call,
          * just fill dispatch params and let user32 do the rest */
@@ -4401,7 +4415,6 @@ static BOOL process_message( struct send_message_info *info, DWORD_PTR *res_ptr,
                                         ansi, info->wm_char );
     }
 
-    thread_info->msg_source = msg_source_unavailable;
     spy_enter_message( SPY_SENDMESSAGE, info->hwnd, info->msg, info->wparam, info->lparam );
 
     if (info->dest_tid != GetCurrentThreadId() ||
@@ -4428,7 +4441,6 @@ static BOOL process_message( struct send_message_info *info, DWORD_PTR *res_ptr,
     }
 
     spy_exit_message( SPY_RESULT_OK, info->hwnd, info->msg, result, info->wparam, info->lparam );
-    thread_info->msg_source = prev_source;
     if (ret && res_ptr) *res_ptr = result;
     return ret;
 }
@@ -4867,6 +4879,6 @@ BOOL WINAPI NtUserGetCurrentInputMessageSource( INPUT_MESSAGE_SOURCE *source )
         RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
         return FALSE;
     }
-    *source = NtUserGetThreadInfo()->msg_source;
+    *source = get_user_thread_info()->client_info->msg_source;
     return TRUE;
 }
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h
index 2f9bc9c2ccc1..1929a7e2cd60 100644
--- a/dlls/win32u/ntuser_private.h
+++ b/dlls/win32u/ntuser_private.h
@@ -74,10 +74,8 @@ typedef struct tagWND
     int                swap_interval; /* OpenGL surface swap interval */
     int                pixel_format;  /* Pixel format set by the graphics driver */
     int                clip_clients;  /* Has client surfaces that needs to be clipped out */
-    int                cbWndExtra;    /* class cbWndExtra at window creation */
+    unsigned int       cbWndExtra;    /* class cbWndExtra at window creation */
     DWORD_PTR          userdata;      /* User private data */
-    int                private_off;   /* offset of private extra bytes range */
-    int                private_len;   /* length of private extra bytes range */
     DWORD              wExtra[1];     /* Window extra bytes */
 } WND;
 
@@ -109,19 +107,25 @@ struct mouse_tracking_info
     POINT last_mouse_message_pos;
 };
 
-/* this is the structure stored in TEB->Win32ClientInfo */
-/* no attempt is made to keep the layout compatible with the Windows one */
+/* internal per-thread data */
 struct user_thread_info
 {
-    struct ntuser_thread_info     client_info;            /* Data shared with client */
+    struct ntuser_thread_info    *client_info;            /* Data shared with client */
     HANDLE                        server_queue;           /* Handle to server-side queue */
     HANDLE                        idle_event;             /* Handle to the process idle event */
     LONGLONG                      last_driver_time;       /* Get/PeekMessage driver event time */
+    HWND                          top_window;             /* desktop window */
+    HWND                          msg_window;             /* HWND_MESSAGE parent window */
+    WORD                          msg_call_depth;         /* SendMessage recursion counter */
     WORD                          hook_call_depth;        /* Number of recursively called hook procs */
     WORD                          hook_unicode;           /* Is current hook unicode? */
     HHOOK                         hook;                   /* Current hook */
     struct received_message_info *receive_info;           /* Message being currently received */
+    UINT                          message_time;           /* value for GetMessageTime */
+    UINT                          message_pos;            /* value for GetMessagePos */
+    LPARAM                        message_extra;          /* value for GetMessageExtraInfo */
     struct imm_thread_data       *imm_thread_data;        /* IMM thread data */
+    HIMC                          default_imc;            /* default input context */
     HKL                           kbd_layout;             /* Current keyboard layout */
     UINT                          kbd_layout_id;          /* Current keyboard layout ID */
     struct hardware_msg_data     *rawinput;               /* Current rawinput message data */
@@ -132,12 +136,7 @@ struct user_thread_info
     struct mouse_tracking_info   *mouse_tracking_info;    /* NtUserTrackMouseEvent handling */
 };
 
-C_ASSERT( sizeof(struct user_thread_info) <= sizeof(((TEB *)0)->Win32ClientInfo) );
-
-static inline struct user_thread_info *get_user_thread_info(void)
-{
-    return CONTAINING_RECORD( NtUserGetThreadInfo(), struct user_thread_info, client_info );
-}
+extern struct user_thread_info *get_user_thread_info(void);
 
 struct hook_extra_info
 {
@@ -198,6 +197,7 @@ struct dce *set_class_dce( struct tagCLASS *class, struct dce *dce );
 extern atom_t wine_server_add_atom( void *req, UNICODE_STRING *str );
 extern BOOL is_desktop_class( UNICODE_STRING *name );
 extern BOOL is_message_class( UNICODE_STRING *name );
+extern ATOM get_builtin_class_atom( enum ntuser_client_procs proc );
 extern void register_builtin_classes(void);
 extern void register_desktop_class(void);
 
@@ -218,6 +218,7 @@ extern BOOL is_cache_dc( HDC hdc );
 
 /* message.c */
 extern void check_for_events( UINT flags );
+extern UINT get_send_message_flags(void);
 
 /* systray.c */
 extern LRESULT system_tray_call( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, void *data );
diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c
index 3b8db3ca2914..3d03b41fc244 100644
--- a/dlls/win32u/opengl.c
+++ b/dlls/win32u/opengl.c
@@ -1930,12 +1930,6 @@ static BOOL win32u_wglMakeContextCurrentARB( HDC draw_hdc, HDC read_hdc, HGLRC c
         else RtlSetLastWin32Error( ERROR_INVALID_HANDLE );
         return FALSE;
     }
-    if (context->format != format)
-    {
-        WARN( "Mismatched draw_hdc %p format %u, context %p format %u\n", draw_hdc, format, context, context->format );
-        RtlSetLastWin32Error( ERROR_INVALID_PIXEL_FORMAT );
-        return FALSE;
-    }
 
     created = create_memory_pbuffer( draw_hdc );
     if (!context_sync_drawables( context, draw_hdc, read_hdc )) return FALSE;
diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c
index 7a74bf31a57e..f33a9b56aebc 100644
--- a/dlls/win32u/rawinput.c
+++ b/dlls/win32u/rawinput.c
@@ -481,7 +481,7 @@ UINT WINAPI NtUserGetRawInputBuffer( RAWINPUT *data, UINT *data_size, UINT heade
         if (!wine_server_call_err( req )) count = reply->count;
         else count = -1;
         *data_size = reply->next_size;
-        if (reply->count) thread_info->client_info.message_time = reply->time;
+        if (reply->count) thread_info->message_time = reply->time;
     }
     SERVER_END_REQ;
 
diff --git a/dlls/win32u/syscall.c b/dlls/win32u/syscall.c
index c0e4b47ca023..e9f36bdd32b2 100644
--- a/dlls/win32u/syscall.c
+++ b/dlls/win32u/syscall.c
@@ -168,6 +168,8 @@ static const char *usercall_names[NtUserCallCount] =
 #undef USER32_CALLBACK_ENTRY
 };
 
+static pthread_key_t user_thread_info_key;
+
 NTSTATUS __wine_unix_lib_init(void)
 {
 #ifdef _WIN64
@@ -181,5 +183,33 @@ NTSTATUS __wine_unix_lib_init(void)
 #endif
     KeAddSystemServiceTable( syscalls, NULL, ARRAY_SIZE(syscalls), arguments, 1 );
     ntdll_add_syscall_debug_info( 1, syscall_names, usercall_names );
+    pthread_key_create( &user_thread_info_key, NULL );
     return STATUS_SUCCESS;
 }
+
+struct user_thread_info *get_user_thread_info(void)
+{
+    struct user_thread_info *info = pthread_getspecific( user_thread_info_key );
+
+    if (!info)
+    {
+        TEB *teb = NtCurrentTeb();
+
+        info = calloc( 1, sizeof(*info) );
+        pthread_setspecific( user_thread_info_key, info );
+
+        if (teb)
+        {
+#ifndef _WIN64
+            if (teb->GdiBatchCount)
+            {
+                TEB64 *teb64 = (TEB64 *)(UINT_PTR)teb->GdiBatchCount;
+                info->client_info = (struct ntuser_thread_info *)teb64->Win32ClientInfo;
+            }
+            else
+#endif
+            info->client_info = (struct ntuser_thread_info *)teb->Win32ClientInfo;
+        }
+    }
+    return info;
+}
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c
index 00fe10f30ca0..f0cff127b26c 100644
--- a/dlls/win32u/sysparams.c
+++ b/dlls/win32u/sysparams.c
@@ -1456,6 +1456,7 @@ const char *gpu_device_name( UINT16 vendor, UINT16 device, const char *default_n
     case MAKELONG(0x8086, 0x193d): return "Intel(R) Iris(TM) Pro Graphics P580";
     case MAKELONG(0x8086, 0x87c0): return "Intel(R) UHD Graphics 617";
     case MAKELONG(0x8086, 0x3ea0): return "Intel(R) UHD Graphics 620";
+    case MAKELONG(0x8086, 0x5917): return "Intel(R) UHD Graphics 620";
     case MAKELONG(0x8086, 0x591e): return "Intel(R) HD Graphics 615";
     case MAKELONG(0x8086, 0x5916): return "Intel(R) HD Graphics 620";
     case MAKELONG(0x8086, 0x5912): return "Intel(R) HD Graphics 630";
@@ -3173,10 +3174,11 @@ static BOOL is_valid_dpi_awareness_context( UINT context, UINT dpi )
 
 UINT get_thread_dpi_awareness_context(void)
 {
-    struct ntuser_thread_info *info = NtUserGetThreadInfo();
+    struct user_thread_info *info = get_user_thread_info();
     UINT context;
 
-    if (!(context = info->dpi_context)) context = ReadNoFence( &dpi_context );
+    if (!info->client_info || !(context = info->client_info->dpi_context))
+        context = ReadNoFence( &dpi_context );
     return context ? context : NTUSER_DPI_UNAWARE;
 }
 
@@ -3209,7 +3211,7 @@ UINT get_system_dpi(void)
 /* keep in sync with user32 */
 UINT set_thread_dpi_awareness_context( UINT context )
 {
-    struct ntuser_thread_info *info = NtUserGetThreadInfo();
+    struct user_thread_info *info = get_user_thread_info();
     UINT prev;
 
     if (!is_valid_dpi_awareness_context( context, system_dpi ))
@@ -3218,9 +3220,11 @@ UINT set_thread_dpi_awareness_context( UINT context )
         return 0;
     }
 
-    if (!(prev = info->dpi_context)) prev = NtUserGetProcessDpiAwarenessContext( GetCurrentProcess() ) | NTUSER_DPI_CONTEXT_FLAG_PROCESS;
-    if (NTUSER_DPI_CONTEXT_GET_FLAGS( context ) & NTUSER_DPI_CONTEXT_FLAG_PROCESS) info->dpi_context = 0;
-    else info->dpi_context = context;
+    if (!info->client_info) return 0;
+    if (!(prev = info->client_info->dpi_context))
+        prev = NtUserGetProcessDpiAwarenessContext( GetCurrentProcess() ) | NTUSER_DPI_CONTEXT_FLAG_PROCESS;
+    if (NTUSER_DPI_CONTEXT_GET_FLAGS( context ) & NTUSER_DPI_CONTEXT_FLAG_PROCESS) info->client_info->dpi_context = 0;
+    else info->client_info->dpi_context = context;
 
     return prev;
 }
@@ -7467,6 +7471,7 @@ static void thread_detach(void)
     if (thread_info->idle_event) NtClose( thread_info->idle_event );
     free( thread_info->session_data );
     free( thread_info->mouse_tracking_info );
+    free( thread_info );
 
     exiting_thread_id = 0;
 }
diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c
index 19c05a978358..4481ffa1b320 100644
--- a/dlls/win32u/tests/win32u.c
+++ b/dlls/win32u/tests/win32u.c
@@ -175,6 +175,16 @@ static void test_window_props(void)
     DestroyWindow( hwnd );
 }
 
+static WNDPROC real_class_wndproc;
+
+static LRESULT WINAPI test_real_class_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
+{
+    LRESULT lr = 0;
+    if (real_class_wndproc) lr = CallWindowProcW( real_class_wndproc, hwnd, msg, wparam, lparam );
+    if (msg == WM_NCCREATE) lr = 1;
+    return lr;
+}
+
 static void test_class(void)
 {
     struct pinned_atom
@@ -416,6 +426,51 @@ static void test_class(void)
         "NtUserGetAtomName returned %lx %lu\n", ret, GetLastError() );
     ok( buf[0] == 0xcccc, "buf = %s\n", debugstr_w(buf) );
 
+
+    memset( &cls, 0, sizeof(cls) );
+    ret = GetClassInfoW( NULL, L"Static", &cls );
+    ok( ret, "GetClassInfoW failed: %lu\n", GetLastError() );
+
+    real_class_wndproc = cls.lpfnWndProc;
+    cls.lpfnWndProc = test_real_class_wndproc;
+    cls.hInstance = GetModuleHandleW( NULL );
+    cls.lpszClassName = L"WineTest Class";
+
+    class = RegisterClassW( &cls );
+    ok( class, "RegisterClassW failed: %lu\n", GetLastError() );
+
+    hwnd = CreateWindowW( cls.lpszClassName, L"test name", WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
+                          CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, NULL, 0 );
+
+    /* Get real class, in this case Static. */
+    memset( buf, 0xcc, sizeof(buf) );
+    name.Buffer = buf;
+    name.Length = 0xdead;
+    name.MaximumLength = sizeof(buf);
+    ret = NtUserGetClassName( hwnd, TRUE, &name );
+    ok( ret == 6, "NtUserGetClassName returned %lu\n", ret );
+    ok( name.Length == 0xdead, "Length = %u\n", name.Length );
+    ok( name.MaximumLength == sizeof(buf), "MaximumLength = %u\n", name.MaximumLength );
+    ok( !wcscmp( buf, L"Static" ), "buf = %s\n", debugstr_w(buf) );
+
+    /* Get normal class instead of real class. */
+    memset( buf, 0xcc, sizeof(buf) );
+    name.Buffer = buf;
+    name.Length = 0xdead;
+    name.MaximumLength = sizeof(buf);
+    ret = NtUserGetClassName( hwnd, FALSE, &name );
+    ok( ret == 14, "NtUserGetClassName returned %lu\n", ret );
+    ok( name.Length == 0xdead, "Length = %u\n", name.Length );
+    ok( name.MaximumLength == sizeof(buf), "MaximumLength = %u\n", name.MaximumLength );
+    ok( !wcscmp( buf, cls.lpszClassName ), "buf = %s\n", debugstr_w(buf) );
+
+    DestroyWindow( hwnd );
+
+    ret = UnregisterClassW( cls.lpszClassName, GetModuleHandleW( NULL ) );
+    ok( ret, "UnregisterClassW failed: %lu\n", GetLastError() );
+    real_class_wndproc = NULL;
+
+
     cls.lpszClassName = L"#1";
     class = RegisterClassW( &cls );
     ok( class == 1, "RegisterClassW failed: %lu\n", GetLastError() );
diff --git a/dlls/win32u/win32syscalls.h b/dlls/win32u/win32syscalls.h
index 3f3d702513db..3e631c74da1e 100644
--- a/dlls/win32u/win32syscalls.h
+++ b/dlls/win32u/win32syscalls.h
@@ -1390,7 +1390,7 @@
     SYSCALL_ENTRY( 0x156a, NtUserSetMenuContextHelpId, 8 ) \
     SYSCALL_ENTRY( 0x156b, NtUserSetMenuDefaultItem, 12 ) \
     SYSCALL_ENTRY( 0x156c, NtUserSetMenuFlagRtoL, 0 ) \
-    SYSCALL_ENTRY( 0x156d, NtUserSetMessageExtraInfo, 0 ) \
+    SYSCALL_ENTRY( 0x156d, NtUserSetMessageExtraInfo, 4 ) \
     SYSCALL_ENTRY( 0x156e, NtUserSetMirrorRendering, 0 ) \
     SYSCALL_ENTRY( 0x156f, NtUserSetModernAppWindow, 0 ) \
     SYSCALL_ENTRY( 0x1570, NtUserSetMonitorWorkArea, 0 ) \
@@ -2932,7 +2932,7 @@
     SYSCALL_ENTRY( 0x156a, NtUserSetMenuContextHelpId, 16 ) \
     SYSCALL_ENTRY( 0x156b, NtUserSetMenuDefaultItem, 24 ) \
     SYSCALL_ENTRY( 0x156c, NtUserSetMenuFlagRtoL, 0 ) \
-    SYSCALL_ENTRY( 0x156d, NtUserSetMessageExtraInfo, 0 ) \
+    SYSCALL_ENTRY( 0x156d, NtUserSetMessageExtraInfo, 8 ) \
     SYSCALL_ENTRY( 0x156e, NtUserSetMirrorRendering, 0 ) \
     SYSCALL_ENTRY( 0x156f, NtUserSetModernAppWindow, 0 ) \
     SYSCALL_ENTRY( 0x1570, NtUserSetMonitorWorkArea, 0 ) \
@@ -3820,7 +3820,6 @@
     SYSCALL_STUB( NtUserGetKeyboardType ) \
     SYSCALL_STUB( NtUserGetListBoxInfo ) \
     SYSCALL_STUB( NtUserGetMenuIndex ) \
-    SYSCALL_STUB( NtUserGetMessagePos ) \
     SYSCALL_STUB( NtUserGetMinuserIdForBaseWindow ) \
     SYSCALL_STUB( NtUserGetModernAppWindow ) \
     SYSCALL_STUB( NtUserGetOemBitmapSize ) \
@@ -4051,7 +4050,6 @@
     SYSCALL_STUB( NtUserSetMagnificationDesktopMagnifierOffsetsDWMUpdated ) \
     SYSCALL_STUB( NtUserSetManipulationInputTarget ) \
     SYSCALL_STUB( NtUserSetMenuFlagRtoL ) \
-    SYSCALL_STUB( NtUserSetMessageExtraInfo ) \
     SYSCALL_STUB( NtUserSetMirrorRendering ) \
     SYSCALL_STUB( NtUserSetModernAppWindow ) \
     SYSCALL_STUB( NtUserSetMonitorWorkArea ) \
diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec
index 49e8b4ab048e..a58fda6af9c2 100644
--- a/dlls/win32u/win32u.spec
+++ b/dlls/win32u/win32u.spec
@@ -1051,7 +1051,7 @@
 @ stub -syscall NtUserGetMenuIndex
 @ stdcall -syscall NtUserGetMenuItemRect(long long long ptr)
 @ stdcall -syscall NtUserGetMessage(ptr long long long)
-@ stub -syscall NtUserGetMessagePos
+@ stdcall -syscall NtUserGetMessagePos()
 @ stub -syscall NtUserGetMinuserIdForBaseWindow
 @ stub -syscall NtUserGetModernAppWindow
 @ stdcall -syscall NtUserGetMouseMovePointsEx(long ptr ptr long long)
@@ -1388,7 +1388,7 @@
 @ stdcall -syscall NtUserSetMenuContextHelpId(long long)
 @ stdcall -syscall NtUserSetMenuDefaultItem(long long long)
 @ stub -syscall NtUserSetMenuFlagRtoL
-@ stub -syscall NtUserSetMessageExtraInfo
+@ stdcall -syscall NtUserSetMessageExtraInfo(long)
 @ stub -syscall NtUserSetMirrorRendering
 @ stub -syscall NtUserSetModernAppWindow
 @ stub -syscall NtUserSetMonitorWorkArea
diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h
index 031e1afec707..be303ae4d131 100644
--- a/dlls/win32u/win32u_private.h
+++ b/dlls/win32u/win32u_private.h
@@ -41,6 +41,7 @@ extern BOOL process_wine_setcursor( HWND hwnd, HWND window, HCURSOR handle );
 extern HICON alloc_cursoricon_handle( BOOL is_icon );
 extern ULONG_PTR get_icon_param( HICON handle );
 extern ULONG_PTR set_icon_param( HICON handle, const struct free_icon_params *params );
+extern HICON create_small_icon( HICON handle );
 
 /* dce.c */
 extern struct window_surface dummy_surface;
@@ -291,6 +292,7 @@ extern BOOL is_zoomed( HWND hwnd );
 extern BOOL set_window_pixel_format( HWND hwnd, int format, BOOL internal );
 extern int get_window_pixel_format( HWND hwnd );
 extern DWORD get_window_long( HWND hwnd, INT offset );
+extern UINT get_window_fnid( HWND hwnd );
 extern ULONG_PTR get_window_long_ptr( HWND hwnd, INT offset, BOOL ansi );
 extern BOOL get_window_rect( HWND hwnd, RECT *rect, UINT dpi );
 enum coords_relative;
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c
index 6cba020e6f3f..4b53686dba13 100644
--- a/dlls/win32u/window.c
+++ b/dlls/win32u/window.c
@@ -410,10 +410,10 @@ BOOL is_client_surface_window( struct client_surface *surface, HWND hwnd )
  */
 HWND get_hwnd_message_parent(void)
 {
-    struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
+    struct user_thread_info *thread_info = get_user_thread_info();
 
     if (!thread_info->msg_window) get_desktop_window(); /* trigger creation */
-    return UlongToHandle( thread_info->msg_window );
+    return thread_info->msg_window;
 }
 
 /***********************************************************************
@@ -442,11 +442,11 @@ HWND get_full_window_handle( HWND hwnd )
  */
 BOOL is_desktop_window( HWND hwnd )
 {
-    struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
+    struct user_thread_info *thread_info = get_user_thread_info();
 
     if (!hwnd) return FALSE;
-    if (hwnd == UlongToHandle( thread_info->top_window )) return TRUE;
-    if (hwnd == UlongToHandle( thread_info->msg_window )) return TRUE;
+    if (hwnd == thread_info->top_window) return TRUE;
+    if (hwnd == thread_info->msg_window) return TRUE;
 
     if (!HIWORD(hwnd) || HIWORD(hwnd) == 0xffff)
     {
@@ -1113,26 +1113,23 @@ static HWND get_last_active_popup( HWND hwnd )
     return retval;
 }
 
-static LONG_PTR get_win_data( const void *ptr, UINT size )
+static BOOL get_window_extra( HWND hwnd, WND *win, UINT offset, UINT size, LONG_PTR *ret, BOOL internal )
 {
-    if (size == sizeof(WORD))
-    {
-        WORD ret;
-        memcpy( &ret, ptr, sizeof(ret) );
-        return ret;
-    }
-    else if (size == sizeof(DWORD))
-    {
-        DWORD ret;
-        memcpy( &ret, ptr, sizeof(ret) );
-        return ret;
-    }
-    else
+    struct object_lock lock = OBJECT_LOCK_INIT;
+    const window_shm_t *window_shm = NULL;
+    BOOL valid = FALSE;
+    UINT status;
+
+    while ((status = get_shared_window( hwnd, &lock, &window_shm )) == STATUS_PENDING)
     {
-        LONG_PTR ret;
-        memcpy( &ret, ptr, sizeof(ret) );
-        return ret;
+        valid = size <= win->cbWndExtra && offset <= win->cbWndExtra - size &&
+                (internal || offset >= window_shm->private_size);
+        if (valid) memcpy( ret, (char *)win->wExtra + offset, size );
     }
+
+    if (status) RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
+    else if (!valid) RtlSetLastWin32Error( ERROR_INVALID_INDEX );
+    return valid;
 }
 
 /* helper for set_window_long */
@@ -1164,9 +1161,15 @@ BOOL is_zoomed( HWND hwnd )
     return (get_window_long( hwnd, GWL_STYLE ) & WS_MAXIMIZE) != 0;
 }
 
-static BOOL in_private_data_range( const WND *win, INT offset, UINT size )
+UINT get_window_fnid( HWND hwnd )
 {
-    return offset < win->private_off + win->private_len && offset + size >= win->private_off;
+    struct object_lock lock = OBJECT_LOCK_INIT;
+    const window_shm_t *window_shm = NULL;
+    UINT status, fnid = 0;
+
+    while ((status = get_shared_window( hwnd, &lock, &window_shm )) == STATUS_PENDING)
+        fnid = window_shm->fnid;
+    return status ? 0 : fnid;
 }
 
 static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi, BOOL internal )
@@ -1232,15 +1235,7 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans
 
     if (offset >= 0)
     {
-        if (offset > (int)(win->cbWndExtra - size) ||
-            (!internal && in_private_data_range( win, offset, size )))
-        {
-            WARN("Invalid offset %d\n", offset );
-            release_win_ptr( win );
-            RtlSetLastWin32Error( ERROR_INVALID_INDEX );
-            return 0;
-        }
-        retval = get_win_data( (char *)win->wExtra + offset, size );
+        if (!get_window_extra( hwnd, win, offset, size, &retval, internal )) retval = 0;
         release_win_ptr( win );
         return retval;
     }
@@ -1491,15 +1486,12 @@ static LONG_PTR set_window_long_internal( HWND hwnd, INT offset, UINT size,
     case GWLP_USERDATA:
         break;
     default:
-        if (offset < 0 || offset > (int)(win->cbWndExtra - size) ||
-            (!internal && in_private_data_range( win, offset, size )))
+        if (!get_window_extra( hwnd, win, offset, size, &retval, internal ))
         {
-            WARN("Invalid offset %d\n", offset );
             release_win_ptr( win );
-            RtlSetLastWin32Error( ERROR_INVALID_INDEX );
             return 0;
         }
-        else if (get_win_data( (char *)win->wExtra + offset, size ) == newval)
+        if (retval == newval)
         {
             /* already set to the same value */
             release_win_ptr( win );
@@ -1587,47 +1579,25 @@ LONG_PTR set_window_long( HWND hwnd, INT offset, UINT size, LONG_PTR newval, BOO
  */
 BOOL WINAPI NtUserSetWindowFNID( HWND hwnd, WORD fnid )
 {
-    int off = FNID_OFF(fnid);
-    int len = FNID_LEN(fnid);
-    WND *win;
     BOOL ret;
 
     TRACE( "%p %x\n", hwnd, fnid );
 
-    if (!(win = get_win_ptr( hwnd )))
+    if (!(fnid & 0x8000) || (fnid & 0x7fff) >= NTUSER_NB_PROCS)
     {
-        RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
-        return FALSE;
-    }
-
-    if (win == WND_DESKTOP || win == WND_OTHER_PROCESS)
-    {
-        RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
+        RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
         return FALSE;
     }
+    if (fnid == get_window_fnid( hwnd )) return TRUE;
 
-    if (win->private_len)
-    {
-        ret = win->private_off == off && win->private_len == len;
-
-        release_win_ptr( win );
-        if (!ret) RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
-        return ret;
-    }
-
-    SERVER_START_REQ( set_window_info )
+    SERVER_START_REQ( set_window_fnid )
     {
         req->handle = wine_server_user_handle( hwnd );
-        req->offset = GWLP_FNID_INTERNAL;
-        req->new_info = fnid;
-        if ((ret = !wine_server_call_err( req )))
-        {
-            win->private_off = off;
-            win->private_len = len;
-        }
+        req->atom = get_builtin_class_atom( fnid & 0x7fff );
+        req->fnid = fnid;
+        ret = !wine_server_call_err( req );
     }
     SERVER_END_REQ;
-    release_win_ptr( win );
     return ret;
 }
 
@@ -5600,20 +5570,19 @@ static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name,
 
     if (!parent)  /* if parent is 0 we don't have a desktop window yet */
     {
-        struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
+        struct user_thread_info *thread_info = get_user_thread_info();
 
         if (is_desktop_class( name ))
         {
-            if (!thread_info->top_window) thread_info->top_window = HandleToUlong( full_parent ? full_parent : handle );
-            else assert( full_parent == UlongToHandle( thread_info->top_window ));
+            if (!thread_info->top_window) thread_info->top_window = full_parent ? full_parent : handle;
+            else assert( full_parent == thread_info->top_window );
             if (!thread_info->top_window) ERR_(win)( "failed to create desktop window\n" );
-            else user_driver->pSetDesktopWindow( UlongToHandle( thread_info->top_window ));
+            else user_driver->pSetDesktopWindow( thread_info->top_window );
             register_builtin_classes();
         }
         else  /* HWND_MESSAGE parent */
         {
-            if (!thread_info->msg_window && !full_parent)
-                thread_info->msg_window = HandleToUlong( handle );
+            if (!thread_info->msg_window && !full_parent) thread_info->msg_window = handle;
         }
     }
 
diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c
index 10b8257b2b77..3da9acdd3aff 100644
--- a/dlls/win32u/winstation.c
+++ b/dlls/win32u/winstation.c
@@ -578,8 +578,8 @@ BOOL WINAPI NtUserSetThreadDesktop( HDESK handle )
         struct session_thread_data *data = get_session_thread_data();
         data->shared_desktop = find_shared_session_object( locator.id, locator.offset );
         memset( &data->shared_foreground, 0, sizeof(data->shared_foreground) );
-        thread_info->client_info.top_window = 0;
-        thread_info->client_info.msg_window = 0;
+        thread_info->top_window = 0;
+        thread_info->msg_window = 0;
         if (was_virtual_desktop != is_virtual_desktop()) update_display_cache( TRUE );
     }
     return ret;
@@ -789,10 +789,10 @@ static inline TEB64 *NtCurrentTeb64(void) { return (TEB64 *)NtCurrentTeb()->GdiB
 
 HWND get_desktop_window(void)
 {
-    struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
+    struct user_thread_info *thread_info = get_user_thread_info();
     BOOL is_service;
 
-    if (thread_info->top_window) return UlongToHandle( thread_info->top_window );
+    if (thread_info->top_window) return thread_info->top_window;
 
     /* don't create an actual explorer desktop window for services */
     is_service = is_service_process();
@@ -802,8 +802,8 @@ HWND get_desktop_window(void)
         req->force = is_service;
         if (!wine_server_call( req ))
         {
-            thread_info->top_window = reply->top_window;
-            thread_info->msg_window = reply->msg_window;
+            thread_info->top_window = wine_server_ptr_handle( reply->top_window );
+            thread_info->msg_window = wine_server_ptr_handle( reply->msg_window );
         }
     }
     SERVER_END_REQ;
@@ -884,18 +884,17 @@ HWND get_desktop_window(void)
             req->force = 1;
             if (!wine_server_call( req ))
             {
-                thread_info->top_window = reply->top_window;
-                thread_info->msg_window = reply->msg_window;
+                thread_info->top_window = wine_server_ptr_handle( reply->top_window );
+                thread_info->msg_window = wine_server_ptr_handle( reply->msg_window );
             }
         }
         SERVER_END_REQ;
     }
-
     if (!thread_info->top_window) ERR_(win)( "failed to create desktop window\n" );
-    else user_driver->pSetDesktopWindow( UlongToHandle( thread_info->top_window ));
+    else user_driver->pSetDesktopWindow( thread_info->top_window );
 
     register_builtin_classes();
-    return UlongToHandle( thread_info->top_window );
+    return thread_info->top_window;
 }
 
 static HANDLE get_winstations_dir_handle(void)
diff --git a/dlls/windows.devices.bluetooth/Makefile.in b/dlls/windows.devices.bluetooth/Makefile.in
index 0e0364f2a3f0..a3823f49023a 100644
--- a/dlls/windows.devices.bluetooth/Makefile.in
+++ b/dlls/windows.devices.bluetooth/Makefile.in
@@ -8,4 +8,6 @@ SOURCES = \
 	bluetoothadapter.c \
 	bluetoothdevice.c \
 	classes.idl \
-	main.c
+	gatt.c \
+	main.c \
+	vector.c
diff --git a/dlls/windows.devices.bluetooth/bluetoothdevice.c b/dlls/windows.devices.bluetooth/bluetoothdevice.c
index 1c1a13c381cb..adc3fddb18df 100644
--- a/dlls/windows.devices.bluetooth/bluetoothdevice.c
+++ b/dlls/windows.devices.bluetooth/bluetoothdevice.c
@@ -18,6 +18,14 @@
  */
 
 #include "private.h"
+#include "roapi.h"
+#include "setupapi.h"
+#include "initguid.h"
+#include "devpkey.h"
+#include "bthledef.h"
+#include "ddk/bthguid.h"
+#include "bluetoothleapis.h"
+
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL( bluetooth );
@@ -155,6 +163,311 @@ static const IBluetoothDeviceStaticsVtbl bluetoothdevice_statics_vtbl =
     bluetoothdevice_statics_GetDeviceSelector,
 };
 
+struct ble_device
+{
+    IBluetoothLEDevice IBluetoothLEDevice_iface;
+    HSTRING id;
+    UINT64 addr;
+    HANDLE device;
+    LONG ref;
+};
+
+static inline struct ble_device *impl_from_IBluetoothLEDevice( IBluetoothLEDevice *iface )
+{
+    return CONTAINING_RECORD( iface, struct ble_device, IBluetoothLEDevice_iface );
+}
+
+static HRESULT WINAPI ble_device_QueryInterface( IBluetoothLEDevice *iface, REFIID iid, void **out )
+{
+    struct ble_device *impl = impl_from_IBluetoothLEDevice( iface );
+
+    TRACE( "(%p, %s, %p)\n", iface, debugstr_guid( iid ), out );
+
+    if (IsEqualGUID( iid, &IID_IUnknown ) ||
+        IsEqualGUID( iid, &IID_IInspectable ) ||
+        IsEqualGUID( iid, &IID_IAgileObject ) ||
+        IsEqualGUID( iid, &IID_IBluetoothLEDevice ))
+    {
+        IBluetoothLEDevice_AddRef( (*out = &impl->IBluetoothLEDevice_iface) );
+        return S_OK;
+    }
+
+    *out = NULL;
+    FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ble_device_AddRef( IBluetoothLEDevice *iface )
+{
+     struct ble_device *impl = impl_from_IBluetoothLEDevice( iface );
+     TRACE( "(%p)\n", iface );
+     return InterlockedIncrement( &impl->ref );
+}
+
+static ULONG WINAPI ble_device_Release( IBluetoothLEDevice *iface )
+{
+    struct ble_device *impl = impl_from_IBluetoothLEDevice( iface );
+    ULONG ref = InterlockedDecrement( &impl->ref );
+    TRACE( "(%p)\n", iface );
+
+    if (!ref)
+    {
+        WindowsDeleteString( impl->id );
+        CloseHandle( impl->device );
+        free( impl );
+    }
+    return ref;
+}
+
+static HRESULT WINAPI ble_device_GetIids( IBluetoothLEDevice *iface, ULONG *iid_count, IID **iids )
+{
+    FIXME( "(%p, %p, %p): stub!\n", iface, iid_count, iids );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ble_device_GetRuntimeClassName( IBluetoothLEDevice *iface, HSTRING *class_name )
+{
+    FIXME( "(%p, %p): stub!\n", iface, class_name );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ble_device_GetTrustLevel( IBluetoothLEDevice *iface, TrustLevel *level )
+{
+    FIXME( "(%p, %p): stub!\n", iface, level );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ble_device_get_DeviceId( IBluetoothLEDevice *iface, HSTRING *value )
+{
+    struct ble_device *impl = impl_from_IBluetoothLEDevice( iface );
+    TRACE( "(%p, %p)\n", iface, value );
+    return WindowsDuplicateString( impl->id, value );
+}
+
+static HRESULT WINAPI ble_device_get_Name( IBluetoothLEDevice *iface, HSTRING *value )
+{
+    FIXME( "(%p, %p): stub!\n", iface, value );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ble_device_get_GattServices( IBluetoothLEDevice *iface, IVectorView_GattDeviceService **services )
+{
+    static const struct vector_iids iids = {
+        .vector = &IID_IVector_IInspectable,
+        .view = &IID_IVectorView_GattDeviceService,
+        .iterable = &IID_IIterable_GattDeviceService,
+        .iterator = &IID_IIterator_GattDeviceService,
+    };
+    struct ble_device *impl = impl_from_IBluetoothLEDevice( iface );
+    BTH_LE_GATT_SERVICE *buf = NULL;
+    IVector_IInspectable *vector;
+    USHORT actual = 0, i;
+    HRESULT hr;
+
+    TRACE( "(%p, %p)\n", iface, services );
+
+    *services = NULL;
+    if (FAILED(hr = vector_create( &iids, (void **)&vector ))) return hr;
+    hr = BluetoothGATTGetServices( impl->device, 0, NULL, &actual, 0 );
+    if (SUCCEEDED( hr ) || hr != HRESULT_FROM_WIN32( ERROR_MORE_DATA )) goto done;
+
+    for (;;)
+    {
+        UINT32 size = actual;
+        void *tmp;
+
+        if (!(tmp = realloc( buf, sizeof( *buf ) * size )))
+        {
+            hr = E_OUTOFMEMORY;
+            goto done;
+        }
+        buf = tmp;
+        if (SUCCEEDED(hr = BluetoothGATTGetServices( impl->device, size, buf, &actual, 0 ))) break;
+        if (hr != HRESULT_FROM_WIN32( ERROR_INVALID_USER_BUFFER )) goto done;
+    }
+    for (i = 0; i < actual; i++)
+    {
+        IGattDeviceService *service;
+
+        if (FAILED(hr = gatt_service_create( &buf[i], &service ))) goto done;
+        hr = IVector_IInspectable_Append( vector, (IInspectable *)service );
+        IGattDeviceService_Release( service );
+        if (FAILED( hr )) goto done;
+    }
+done:
+    free( buf );
+    if (FAILED( hr ))
+    {
+        IVector_IInspectable_Release( vector );
+        return hr;
+    }
+    hr = IVector_IInspectable_GetView( vector, (IVectorView_IInspectable **)services );
+    IVector_IInspectable_Release( vector );
+    return hr;
+}
+
+static HRESULT WINAPI ble_device_get_ConnectionStatus( IBluetoothLEDevice *iface, BluetoothConnectionStatus *value )
+{
+    FIXME( "(%p, %p): stub!\n", iface, value );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ble_device_get_BluetoothAddress( IBluetoothLEDevice *iface, UINT64 *value )
+{
+    struct ble_device *impl = impl_from_IBluetoothLEDevice( iface );
+    TRACE( "(%p, %p)\n", iface, value );
+    *value = impl->addr;
+    return S_OK;
+}
+
+static HRESULT WINAPI ble_device_GetGattService( IBluetoothLEDevice *iface, GUID uuid, IGattDeviceService **service )
+{
+    FIXME( "(%p, %s, %p): stub!\n", iface, debugstr_guid( &uuid ), service );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ble_device_add_NameChanged( IBluetoothLEDevice *iface, ITypedEventHandler_BluetoothLEDevice_IInspectable *handler,
+                                                  EventRegistrationToken *token )
+{
+    FIXME( "(%p, %p, %p): stub!\n", iface, handler, token );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ble_device_remove_NameChanged( IBluetoothLEDevice *iface, EventRegistrationToken token )
+{
+    FIXME( "(%p, %I64d): stub!\n", iface, token.value );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ble_device_add_GattServicesChanged( IBluetoothLEDevice *iface, ITypedEventHandler_BluetoothLEDevice_IInspectable *handler,
+                                                          EventRegistrationToken *token )
+{
+    FIXME( "(%p, %p, %p): stub!\n", iface, handler, token );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ble_device_remove_GattServicesChanged( IBluetoothLEDevice *iface, EventRegistrationToken token )
+{
+    FIXME( "(%p, %I64d): stub!\n", iface, token.value );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ble_device_add_ConnectionStatusChanged( IBluetoothLEDevice *iface, ITypedEventHandler_BluetoothLEDevice_IInspectable *handler,
+                                                              EventRegistrationToken *token )
+{
+    FIXME( "(%p, %p, %p): stub!\n", iface, handler, token );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ble_device_remove_ConnectionStatusChanged( IBluetoothLEDevice *iface, EventRegistrationToken token )
+{
+    FIXME( "(%p, %I64d): stub!\n", iface, token.value );
+    return E_NOTIMPL;
+}
+
+static const IBluetoothLEDeviceVtbl ble_device_vtbl = {
+    /* IUnknown */
+    ble_device_QueryInterface,
+    ble_device_AddRef,
+    ble_device_Release,
+    /* IInspectable */
+    ble_device_GetIids,
+    ble_device_GetRuntimeClassName,
+    ble_device_GetTrustLevel,
+    /* IBluetoothLEDevice */
+    ble_device_get_DeviceId,
+    ble_device_get_Name,
+    ble_device_get_GattServices,
+    ble_device_get_ConnectionStatus,
+    ble_device_get_BluetoothAddress,
+    ble_device_GetGattService,
+    ble_device_add_NameChanged,
+    ble_device_remove_NameChanged,
+    ble_device_add_GattServicesChanged,
+    ble_device_remove_GattServicesChanged,
+    ble_device_add_ConnectionStatusChanged,
+    ble_device_remove_ConnectionStatusChanged,
+};
+
+static HRESULT ble_device_create( IBluetoothLEDevice **device, const WCHAR *id, UINT64 addr )
+{
+    struct ble_device *impl;
+    HRESULT hr;
+
+    TRACE( "(%p, %s, %I64x)\n", device, debugstr_w( id ), addr );
+
+    if (!(impl = calloc( 1, sizeof( *impl ))))
+        return E_OUTOFMEMORY;
+    if (FAILED(hr = WindowsCreateString( id, wcslen( id ), &impl->id )))
+    {
+        free( impl );
+        return hr;
+    }
+    impl->device = CreateFileW( id, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
+    if (impl->device == INVALID_HANDLE_VALUE)
+    {
+        WindowsDeleteString( impl->id );
+        free( impl );
+        return HRESULT_FROM_WIN32( GetLastError() );
+    }
+    impl->ref = 1;
+    impl->addr = addr;
+    impl->IBluetoothLEDevice_iface.lpVtbl = &ble_device_vtbl;
+    *device = &impl->IBluetoothLEDevice_iface;
+    return S_OK;
+}
+
+static HRESULT bluetoothledevice_get_device_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result, BOOL called_async )
+{
+    char buffer[sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA_W ) + MAX_PATH * sizeof( WCHAR )];
+    SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)buffer;
+    SP_DEVICE_INTERFACE_DATA iface_data = { .cbSize = sizeof( iface_data ) };
+    IBluetoothLEDevice *device;
+    BOOL found = FALSE;
+    HDEVINFO devinfo;
+    DWORD idx = 0;
+    UINT64 addr;
+    HRESULT hr;
+
+    if (!called_async) return STATUS_PENDING;
+    if (FAILED(hr = IPropertyValue_GetUInt64( (IPropertyValue *)param, &addr )))
+        return hr;
+
+    devinfo = SetupDiGetClassDevsW( &GUID_BLUETOOTHLE_DEVICE_INTERFACE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
+    if (devinfo == INVALID_HANDLE_VALUE)
+        return HRESULT_FROM_WIN32( GetLastError() );
+    iface_detail->cbSize = sizeof( *iface_detail );
+    result->vt = VT_NULL;
+    while (SetupDiEnumDeviceInterfaces( devinfo, NULL, &GUID_BLUETOOTHLE_DEVICE_INTERFACE, idx++, &iface_data ))
+    {
+        SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof( devinfo_data ) };
+        WCHAR addr_str[13];
+        DEVPROPTYPE type;
+        UINT64 addr2 = 0;
+
+        if (!SetupDiGetDeviceInterfaceDetailW( devinfo, &iface_data, iface_detail, sizeof( buffer ), NULL, &devinfo_data ))
+            continue;
+        if (!SetupDiGetDevicePropertyW( devinfo, &devinfo_data, &DEVPKEY_Bluetooth_DeviceAddress, &type, (BYTE *)addr_str, sizeof( addr_str ), NULL, 0 ))
+            continue;
+        if (swscanf( addr_str, L"%I64x", &addr2 ) != 1)
+            continue;
+        if (addr == addr2)
+        {
+            found = TRUE;
+            break;
+        }
+    }
+
+    SetupDiDestroyDeviceInfoList( devinfo );
+    if (!found)
+        return S_OK;
+    if (FAILED(hr = ble_device_create( &device, iface_detail->DevicePath, addr )))
+        return hr;
+    result->vt = VT_UNKNOWN;
+    result->punkVal = (IUnknown *)device;
+    return S_OK;
+}
+
 struct bluetoothledevice_statics
 {
     IActivationFactory IActivationFactory_iface;
@@ -251,10 +564,30 @@ static HRESULT WINAPI bluetoothledevice_statics_FromIdAsync( IBluetoothLEDeviceS
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI bluetoothledevice_statics_FromBluetoothAddressAsync( IBluetoothLEDeviceStatics *iface, UINT64 addr, IAsyncOperation_BluetoothLEDevice **async_op )
+static HRESULT WINAPI bluetoothledevice_statics_FromBluetoothAddressAsync( IBluetoothLEDeviceStatics *iface,
+                                                                           UINT64 addr,
+                                                                           IAsyncOperation_BluetoothLEDevice **async_op )
 {
-    FIXME( "(%p, %#I64x, %p): stub!\n", iface, addr, async_op );
-    return E_NOTIMPL;
+    static const WCHAR *class_name = RuntimeClass_Windows_Foundation_PropertyValue;
+    IPropertyValueStatics *statics;
+    IPropertyValue *addr_val;
+    HSTRING_HEADER hdr;
+    HSTRING str;
+    HRESULT hr;
+
+    TRACE( "(%p, %#I64x, %p)\n", iface, addr, async_op );
+
+    if (FAILED(hr = WindowsCreateStringReference( class_name, wcslen( class_name ), &hdr, &str ))) return hr;
+    if (FAILED(hr = RoGetActivationFactory( str, &IID_IPropertyValueStatics, (void **)&statics ))) return hr;
+    if (FAILED(hr = IPropertyValueStatics_CreateUInt64( statics, addr, (IInspectable **)&addr_val )))
+    {
+        IPropertyValueStatics_Release( statics );
+        return hr;
+    }
+    IPropertyValueStatics_Release( statics );
+
+    return async_operation_inspectable_create( &IID_IAsyncOperation_BluetoothLEDevice, (IUnknown *)iface, (IUnknown *)addr_val, bluetoothledevice_get_device_async,
+                                               (IAsyncOperation_IInspectable **)async_op );
 }
 
 static HRESULT WINAPI bluetoothledevice_statics_GetDeviceSelector( IBluetoothLEDeviceStatics *iface, HSTRING *result )
diff --git a/dlls/windows.devices.bluetooth/gatt.c b/dlls/windows.devices.bluetooth/gatt.c
new file mode 100644
index 000000000000..84b8ee84d5e6
--- /dev/null
+++ b/dlls/windows.devices.bluetooth/gatt.c
@@ -0,0 +1,165 @@
+/* IGatt* Implementation
+ *
+ * Copyright 2026 Vibhav Pant
+ *
+ * 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 "private.h"
+#include "bthledef.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL( bluetooth );
+
+struct gatt_service
+{
+    IGattDeviceService IGattDeviceService_iface;
+    LONG ref;
+
+    BTH_LE_GATT_SERVICE service;
+};
+
+static inline struct gatt_service *impl_from_IGattDeviceService( IGattDeviceService *iface )
+{
+    return CONTAINING_RECORD( iface, struct gatt_service, IGattDeviceService_iface );
+}
+
+static HRESULT WINAPI gatt_service_QueryInterface( IGattDeviceService *iface, REFIID iid, void **out )
+{
+    struct gatt_service *impl = impl_from_IGattDeviceService( iface );
+
+    TRACE( "(%p, %s, %p)\n", iface, debugstr_guid( iid ), out );
+
+    if (IsEqualGUID( iid, &IID_IUnknown) ||
+        IsEqualGUID( iid, &IID_IInspectable ) ||
+        IsEqualGUID( iid, &IID_IAgileObject ) ||
+        IsEqualGUID( iid, &IID_IGattDeviceService ))
+    {
+        IGattDeviceService_AddRef(( *out = &impl->IGattDeviceService_iface ));
+        return S_OK;
+    }
+    *out = NULL;
+    FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI gatt_service_AddRef( IGattDeviceService *iface )
+{
+    struct gatt_service *impl = impl_from_IGattDeviceService( iface );
+    TRACE( "(%p)\n", iface );
+    return InterlockedIncrement( &impl->ref );
+}
+
+static ULONG WINAPI gatt_service_Release( IGattDeviceService *iface )
+{
+    struct gatt_service *impl = impl_from_IGattDeviceService( iface );
+    ULONG ref = InterlockedDecrement( &impl->ref );
+    TRACE( "(%p)\n", iface );
+    if (!ref)
+        free( impl );
+    return ref;
+}
+
+static HRESULT WINAPI gatt_service_GetIids( IGattDeviceService *iface, ULONG *iid_count, IID **iids )
+{
+    FIXME( "(%p, %p, %p): stub!\n", iface, iid_count, iids );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI gatt_service_GetRuntimeClassName( IGattDeviceService *iface, HSTRING *class_name )
+{
+    FIXME( "(%p, %p): stub!\n", iface, class_name );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI gatt_service_GetTrustLevel( IGattDeviceService *iface, TrustLevel *level )
+{
+    FIXME( "(%p, %p): stub!\n", iface, level );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI gatt_service_GetCharacteristics( IGattDeviceService *iface, GUID uuid, IVectorView_GattCharacteristic **chars )
+{
+    FIXME( "(%p, %s, %p): stub!\n", iface, debugstr_guid( &uuid ), chars );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI gatt_service_GetIncludedServices( IGattDeviceService *iface, GUID uuid, IVectorView_GattDeviceService **services )
+{
+    FIXME( "(%p, %s, %p): stub!\n", iface, debugstr_guid( &uuid ), services );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI gatt_service_get_DeviceId( IGattDeviceService *iface, HSTRING *value )
+{
+    FIXME( "(%p, %p): stub!\n", iface, value );
+    *value = NULL;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI gatt_service_get_Uuid( IGattDeviceService *iface, GUID *value )
+{
+    struct gatt_service *impl = impl_from_IGattDeviceService( iface );
+    TRACE( "(%p, %p)\n", iface, value );
+
+    if (impl->service.ServiceUuid.IsShortUuid)
+    {
+        *value = BTH_LE_ATT_BLUETOOTH_BASE_GUID;
+        value->Data1 = impl->service.ServiceUuid.Value.ShortUuid;
+    }
+    else
+        *value = impl->service.ServiceUuid.Value.LongUuid;
+    return S_OK;
+}
+
+static HRESULT WINAPI gatt_service_get_AttributeHandle( IGattDeviceService *iface, UINT16 *value )
+{
+    struct gatt_service *impl = impl_from_IGattDeviceService( iface );
+    TRACE( "(%p, %p)\n", iface, value );
+    *value = impl->service.AttributeHandle;
+    return S_OK;
+}
+
+static const IGattDeviceServiceVtbl gatt_service_vtbl =
+{
+    /* IUnknown */
+    gatt_service_QueryInterface,
+    gatt_service_AddRef,
+    gatt_service_Release,
+    /* IInspectable */
+    gatt_service_GetIids,
+    gatt_service_GetRuntimeClassName,
+    gatt_service_GetTrustLevel,
+    /* IGattDeviceService */
+    gatt_service_GetCharacteristics,
+    gatt_service_GetIncludedServices,
+    gatt_service_get_DeviceId,
+    gatt_service_get_Uuid,
+    gatt_service_get_AttributeHandle
+};
+
+HRESULT gatt_service_create( const BTH_LE_GATT_SERVICE *svc, IGattDeviceService **service )
+{
+    struct gatt_service *impl;
+
+    if (!(impl = calloc( 1, sizeof( *impl ) )))
+        return E_OUTOFMEMORY;
+    impl->IGattDeviceService_iface.lpVtbl = &gatt_service_vtbl;
+    impl->ref = 1;
+    impl->service = *svc;
+    *service = &impl->IGattDeviceService_iface;
+    return S_OK;
+}
diff --git a/dlls/windows.devices.bluetooth/private.h b/dlls/windows.devices.bluetooth/private.h
index 48cc5c044478..b0d86775b759 100644
--- a/dlls/windows.devices.bluetooth/private.h
+++ b/dlls/windows.devices.bluetooth/private.h
@@ -26,6 +26,7 @@
 #include "windef.h"
 #include "winbase.h"
 #include "winstring.h"
+#include "bthledef.h"
 
 #include "activation.h"
 #include "bthdef.h"
@@ -41,6 +42,8 @@
 #define WIDL_using_Windows_Devices_Radios
 #include "windows.devices.radios.h"
 #define WIDL_using_Windows_Devices_Bluetooth
+#define WIDL_using_Windows_Devices_Bluetooth_GenericAttributeProfile
+#include "windows.devices.bluetooth.genericattributeprofile.h"
 #include "windows.devices.bluetooth.rfcomm.h"
 #include "windows.devices.bluetooth.h"
 #define WIDL_using_Windows_Devices_Bluetooth_Advertisement
@@ -56,6 +59,17 @@ extern IActivationFactory *advertisement_watcher_factory;
 HRESULT async_operation_inspectable_create( const GUID *iid, IUnknown *invoker, IUnknown *param,
                                             async_operation_callback callback, IAsyncOperation_IInspectable **out );
 
+struct vector_iids
+{
+    const GUID *iterable;
+    const GUID *iterator;
+    const GUID *vector;
+    const GUID *view;
+};
+HRESULT vector_create( const struct vector_iids *iids, void **out );
+
+HRESULT gatt_service_create( const BTH_LE_GATT_SERVICE *svc, IGattDeviceService **service );
+
 #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr )             \
     static inline impl_type *impl_from( iface_type *iface )                                        \
     {                                                                                              \
diff --git a/dlls/windows.devices.bluetooth/tests/Makefile.in b/dlls/windows.devices.bluetooth/tests/Makefile.in
index c04dafe56d13..2fef3f491edb 100644
--- a/dlls/windows.devices.bluetooth/tests/Makefile.in
+++ b/dlls/windows.devices.bluetooth/tests/Makefile.in
@@ -1,5 +1,5 @@
 TESTDLL = windows.devices.bluetooth.dll
-IMPORTS = combase bluetoothapis
+IMPORTS = combase setupapi bluetoothapis
 
 SOURCES = \
 	bluetooth.c
diff --git a/dlls/windows.devices.bluetooth/tests/bluetooth.c b/dlls/windows.devices.bluetooth/tests/bluetooth.c
index 24ead71caa44..ca5ced41017f 100644
--- a/dlls/windows.devices.bluetooth/tests/bluetooth.c
+++ b/dlls/windows.devices.bluetooth/tests/bluetooth.c
@@ -24,8 +24,12 @@
 #include "windef.h"
 #include "winbase.h"
 #include "winstring.h"
+#include "setupapi.h"
 #include "bthsdpdef.h"
 #include "bluetoothapis.h"
+#include "bthledef.h"
+#include "devpkey.h"
+#include "ddk/bthguid.h"
 
 #include "roapi.h"
 
@@ -37,6 +41,7 @@
 #include "windows.networking.h"
 #define WIDL_using_Windows_Devices_Bluetooth_Advertisement
 #define WIDL_using_Windows_Devices_Bluetooth
+#define WIDL_using_Windows_Devices_Bluetooth_GenericAttributeProfile
 #include "windows.devices.bluetooth.advertisement.h"
 #include "windows.devices.bluetooth.rfcomm.h"
 #include "windows.devices.bluetooth.h"
@@ -524,15 +529,70 @@ static void test_BluetoothDeviceStatics( void )
     IBluetoothDeviceStatics_Release( statics );
 }
 
+static void test_IGattDeviceService( int line, IGattDeviceService *service )
+{
+    HSTRING str = NULL;
+    UINT16 handle;
+    HRESULT hr;
+    GUID uuid;
+
+    hr = IGattDeviceService_get_DeviceId( service, &str );
+    todo_wine ok_( __FILE__, line )( hr == S_OK, "got hr %#lx.\n", hr );
+    todo_wine ok_( __FILE__, line )( !WindowsIsStringEmpty( str ), "got empty DeviceId value.\n" );
+    if (hr == S_OK)
+        trace( "DeviceId: %s\n", debugstr_hstring( str ) );
+    WindowsDeleteString( str );
+
+    hr = IGattDeviceService_get_Uuid( service, &uuid );
+    ok_( __FILE__, line )( hr == S_OK, "got hr %#lx.\n", hr );
+    trace( "UUID: %s\n", debugstr_guid( &uuid ) );
+
+    hr = IGattDeviceService_get_AttributeHandle( service, &handle );
+    ok_( __FILE__, line )( hr == S_OK, "got hr %#lx.\n", hr );
+    trace( "Attribute handle: %#x\n", handle );
+}
+
+static void test_IBluetoothLEDevice( int line, IBluetoothLEDevice *device, UINT64 addr )
+{
+    BluetoothConnectionStatus status;
+    HSTRING str = NULL;
+    UINT64 addr2 = 0;
+    HRESULT hr;
+
+    hr = IBluetoothLEDevice_get_DeviceId( device, &str );
+    ok_( __FILE__, line )( hr == S_OK, "got hr %#lx.\n", hr );
+    ok_( __FILE__, line )( !WindowsIsStringEmpty( str ), "got empty DeviceId value.\n" );
+    trace( "DeviceId: %s\n", debugstr_hstring( str ) );
+    WindowsDeleteString( str );
+
+    hr = IBluetoothLEDevice_get_BluetoothAddress( device, &addr2 );
+    ok_( __FILE__, line )( hr == S_OK, "got hr %#lx.\n", hr );
+    ok_( __FILE__, line )( addr == addr2, "%I64x != %I64x\n", addr, addr2 );
+
+    hr = IBluetoothLEDevice_get_Name( device, &str );
+    todo_wine ok_( __FILE__, line )( hr == S_OK, "got hr %#lx.\n", hr );
+    if (hr == S_OK)
+        trace( "Name: %s\n", debugstr_hstring( str ) );
+    WindowsDeleteString( str );
+
+    hr = IBluetoothLEDevice_get_ConnectionStatus( device, &status );
+    todo_wine ok_( __FILE__, line )( hr == S_OK, "got hr %#lx.\n", hr );
+    if (hr == S_OK)
+        trace( "ConnectionStatus: %d\n", status );
+}
+
 static void test_BluetoothLEDeviceStatics( void )
 {
     static const WCHAR *class_name = RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice;
     IAsyncOperation_BluetoothLEDevice *async_op;
+    SP_DEVICE_INTERFACE_DATA iface_data;
     IBluetoothLEDeviceStatics *statics;
     IActivationFactory *factory;
     IBluetoothLEDevice *device = NULL;
+    HDEVINFO devinfo;
     HSTRING str;
     HRESULT hr;
+    DWORD err, idx = 0;
 
     WindowsCreateString( class_name, wcslen( class_name ), &str );
     hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void *)&factory );
@@ -559,16 +619,66 @@ static void test_BluetoothLEDeviceStatics( void )
 
     /* Use an invalid Bluetooth address. */
     hr = IBluetoothLEDeviceStatics_FromBluetoothAddressAsync( statics, 0, &async_op );
-    todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
 
-    if (hr == S_OK)
+    await_bluetoothledevice( __LINE__, async_op );
+    check_bluetoothledevice_async( __LINE__, async_op, 1, Completed, S_OK, TRUE, &device );
+    ok( !device, "got device %p != NULL\n", device );
+    if (device) IBluetoothLEDevice_Release( device );
+
+    /* Enumerate through all known LE devices on this system. */
+    devinfo = SetupDiGetClassDevsW( &GUID_BLUETOOTHLE_DEVICE_INTERFACE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
+    err = GetLastError();
+    ok( devinfo != INVALID_HANDLE_VALUE, "SetupDiGetClassDevsW failed: %lu\n", err );
+    iface_data.cbSize = sizeof( iface_data );
+    while (SetupDiEnumDeviceInterfaces( devinfo, NULL, &GUID_BLUETOOTHLE_DEVICE_INTERFACE, idx++, &iface_data ))
     {
+        IVectorView_GattDeviceService *services;
+        SP_DEVINFO_DATA devinfo_data;
+        WCHAR addr_str[13];
+        UINT32 i, len = 0;
+        DEVPROPTYPE type;
+        UINT64 addr = 0;
+        BOOL success;
+
+        devinfo_data.cbSize = sizeof( devinfo_data );
+        success = SetupDiGetDeviceInterfaceDetailW( devinfo, &iface_data, NULL, 0, NULL, &devinfo_data );
+        err = GetLastError();
+        ok( !success && err == ERROR_INSUFFICIENT_BUFFER, "SetupDiGetDeviceInterfaceDetailW failed: %lu\n", err );
+        addr_str[0] = '\0';
+        success = SetupDiGetDevicePropertyW( devinfo, &devinfo_data, &DEVPKEY_Bluetooth_DeviceAddress, &type, (BYTE *)addr_str, sizeof( addr_str ), NULL, 0 );
+        err = GetLastError();
+        ok( success, "SetupDiGetDevicePropertyW failed: %lu\n", err );
+        swscanf( addr_str, L"%I64x", &addr );
+
+        winetest_push_context( "device %lu (%s)", idx - 1, debugstr_w( addr_str ) );
+        hr = IBluetoothLEDeviceStatics_FromBluetoothAddressAsync( statics, addr, &async_op );
+        ok( hr == S_OK, "got hr %#lx.\n", hr );
+
         await_bluetoothledevice( __LINE__, async_op );
-        check_bluetoothledevice_async( __LINE__, async_op, 1, Completed, S_OK, TRUE, &device );
-        ok( !device, "got device %p != NULL\n", device );
-        if (device) IBluetoothLEDevice_Release( device );
+        check_bluetoothledevice_async( __LINE__, async_op, 1, Completed, S_OK, FALSE, &device );
+        IAsyncOperation_BluetoothLEDevice_Release( async_op );
+
+        test_IBluetoothLEDevice( __LINE__, device, addr );
+        hr = IBluetoothLEDevice_get_GattServices( device, &services );
+        ok( hr == S_OK, "got hr %#lx.\n", hr );
+        IVectorView_GattDeviceService_get_Size( services, &len );
+        for (i = 0; i < len; i++)
+        {
+            IGattDeviceService *service;
+
+            winetest_push_context( "GATT service %u", i );
+            IVectorView_GattDeviceService_GetAt( services, i, &service );
+            test_IGattDeviceService( __LINE__, service );
+            winetest_pop_context();
+            IGattDeviceService_Release( service );
+        }
+
+        IBluetoothLEDevice_Release( device );
+        winetest_pop_context();
     }
 
+    SetupDiDestroyDeviceInfoList( devinfo );
     IBluetoothLEDeviceStatics_Release( statics );
 }
 
diff --git a/dlls/windows.devices.bluetooth/vector.c b/dlls/windows.devices.bluetooth/vector.c
new file mode 100644
index 000000000000..f0aa937995ba
--- /dev/null
+++ b/dlls/windows.devices.bluetooth/vector.c
@@ -0,0 +1,675 @@
+/* WinRT IVector<IInspectable *> implementation
+ *
+ * Copyright 2021 Rémi Bernon 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 "private.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(combase);
+
+struct iterator
+{
+    IIterator_IInspectable IIterator_IInspectable_iface;
+    const GUID *iid;
+    LONG ref;
+
+    IVectorView_IInspectable *view;
+    UINT32 index;
+    UINT32 size;
+};
+
+static inline struct iterator *impl_from_IIterator_IInspectable( IIterator_IInspectable *iface )
+{
+    return CONTAINING_RECORD( iface, struct iterator, IIterator_IInspectable_iface );
+}
+
+static HRESULT WINAPI iterator_QueryInterface( IIterator_IInspectable *iface, REFIID iid, void **out )
+{
+    struct iterator *impl = impl_from_IIterator_IInspectable( 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, impl->iid ))
+    {
+        IInspectable_AddRef( (*out = &impl->IIterator_IInspectable_iface) );
+        return S_OK;
+    }
+
+    FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI iterator_AddRef( IIterator_IInspectable *iface )
+{
+    struct iterator *impl = impl_from_IIterator_IInspectable( iface );
+    ULONG ref = InterlockedIncrement( &impl->ref );
+    TRACE( "iface %p increasing refcount to %lu.\n", iface, ref );
+    return ref;
+}
+
+static ULONG WINAPI iterator_Release( IIterator_IInspectable *iface )
+{
+    struct iterator *impl = impl_from_IIterator_IInspectable( iface );
+    ULONG ref = InterlockedDecrement( &impl->ref );
+
+    TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref );
+
+    if (!ref)
+    {
+        IVectorView_IInspectable_Release( impl->view );
+        free( impl );
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI iterator_GetIids( IIterator_IInspectable *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 iterator_GetRuntimeClassName( IIterator_IInspectable *iface, HSTRING *class_name )
+{
+    FIXME( "iface %p, class_name %p stub!\n", iface, class_name );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI iterator_GetTrustLevel( IIterator_IInspectable *iface, TrustLevel *trust_level )
+{
+    FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI iterator_get_Current( IIterator_IInspectable *iface, IInspectable **value )
+{
+    struct iterator *impl = impl_from_IIterator_IInspectable( iface );
+    TRACE( "iface %p, value %p.\n", iface, value );
+    return IVectorView_IInspectable_GetAt( impl->view, impl->index, value );
+}
+
+static HRESULT WINAPI iterator_get_HasCurrent( IIterator_IInspectable *iface, boolean *value )
+{
+    struct iterator *impl = impl_from_IIterator_IInspectable( iface );
+
+    TRACE( "iface %p, value %p.\n", iface, value );
+
+    *value = impl->index < impl->size;
+    return S_OK;
+}
+
+static HRESULT WINAPI iterator_MoveNext( IIterator_IInspectable *iface, boolean *value )
+{
+    struct iterator *impl = impl_from_IIterator_IInspectable( iface );
+
+    TRACE( "iface %p, value %p.\n", iface, value );
+
+    if (impl->index < impl->size) impl->index++;
+    return IIterator_IInspectable_get_HasCurrent( iface, value );
+}
+
+static HRESULT WINAPI iterator_GetMany( IIterator_IInspectable *iface, UINT32 items_size,
+                                        IInspectable **items, UINT *count )
+{
+    struct iterator *impl = impl_from_IIterator_IInspectable( iface );
+    TRACE( "iface %p, items_size %u, items %p, count %p.\n", iface, items_size, items, count );
+    return IVectorView_IInspectable_GetMany( impl->view, impl->index, items_size, items, count );
+}
+
+static const IIterator_IInspectableVtbl iterator_vtbl =
+{
+    iterator_QueryInterface,
+    iterator_AddRef,
+    iterator_Release,
+    /* IInspectable methods */
+    iterator_GetIids,
+    iterator_GetRuntimeClassName,
+    iterator_GetTrustLevel,
+    /* IIterator<IInspectable*> methods */
+    iterator_get_Current,
+    iterator_get_HasCurrent,
+    iterator_MoveNext,
+    iterator_GetMany,
+};
+
+struct vector_view
+{
+    IVectorView_IInspectable IVectorView_IInspectable_iface;
+    IIterable_IInspectable IIterable_IInspectable_iface;
+    struct vector_iids iids;
+    LONG ref;
+
+    UINT32 size;
+    IInspectable *elements[1];
+};
+
+static inline struct vector_view *impl_from_IVectorView_IInspectable( IVectorView_IInspectable *iface )
+{
+    return CONTAINING_RECORD( iface, struct vector_view, IVectorView_IInspectable_iface );
+}
+
+static HRESULT WINAPI vector_view_QueryInterface( IVectorView_IInspectable *iface, REFIID iid, void **out )
+{
+    struct vector_view *impl = impl_from_IVectorView_IInspectable( 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, impl->iids.view ))
+    {
+        IInspectable_AddRef( (*out = &impl->IVectorView_IInspectable_iface) );
+        return S_OK;
+    }
+
+    if (IsEqualGUID( iid, impl->iids.iterable ))
+    {
+        IInspectable_AddRef( (*out = &impl->IIterable_IInspectable_iface) );
+        return S_OK;
+    }
+
+    FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI vector_view_AddRef( IVectorView_IInspectable *iface )
+{
+    struct vector_view *impl = impl_from_IVectorView_IInspectable( iface );
+    ULONG ref = InterlockedIncrement( &impl->ref );
+    TRACE( "iface %p increasing refcount to %lu.\n", iface, ref );
+    return ref;
+}
+
+static ULONG WINAPI vector_view_Release( IVectorView_IInspectable *iface )
+{
+    struct vector_view *impl = impl_from_IVectorView_IInspectable( iface );
+    ULONG i, ref = InterlockedDecrement( &impl->ref );
+
+    TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref );
+
+    if (!ref)
+    {
+        for (i = 0; i < impl->size; ++i) IInspectable_Release( impl->elements[i] );
+        free( impl );
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI vector_view_GetIids( IVectorView_IInspectable *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 vector_view_GetRuntimeClassName( IVectorView_IInspectable *iface, HSTRING *class_name )
+{
+    FIXME( "iface %p, class_name %p stub!\n", iface, class_name );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI vector_view_GetTrustLevel( IVectorView_IInspectable *iface, TrustLevel *trust_level )
+{
+    FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI vector_view_GetAt( IVectorView_IInspectable *iface, UINT32 index, IInspectable **value )
+{
+    struct vector_view *impl = impl_from_IVectorView_IInspectable( iface );
+
+    TRACE( "iface %p, index %u, value %p.\n", iface, index, value );
+
+    *value = NULL;
+    if (index >= impl->size) return E_BOUNDS;
+
+    IInspectable_AddRef( (*value = impl->elements[index]) );
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_view_get_Size( IVectorView_IInspectable *iface, UINT32 *value )
+{
+    struct vector_view *impl = impl_from_IVectorView_IInspectable( iface );
+
+    TRACE( "iface %p, value %p.\n", iface, value );
+
+    *value = impl->size;
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_view_IndexOf( IVectorView_IInspectable *iface, IInspectable *element,
+                                           UINT32 *index, BOOLEAN *found )
+{
+    struct vector_view *impl = impl_from_IVectorView_IInspectable( iface );
+    ULONG i;
+
+    TRACE( "iface %p, element %p, index %p, found %p.\n", iface, element, index, found );
+
+    for (i = 0; i < impl->size; ++i) if (impl->elements[i] == element) break;
+    if ((*found = (i < impl->size))) *index = i;
+    else *index = 0;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_view_GetMany( IVectorView_IInspectable *iface, UINT32 start_index,
+                                           UINT32 items_size, IInspectable **items, UINT *count )
+{
+    struct vector_view *impl = impl_from_IVectorView_IInspectable( iface );
+    UINT32 i;
+
+    TRACE( "iface %p, start_index %u, items_size %u, items %p, count %p.\n",
+           iface, start_index, items_size, items, count );
+
+    if (start_index >= impl->size) return E_BOUNDS;
+
+    for (i = start_index; i < impl->size; ++i)
+    {
+        if (i - start_index >= items_size) break;
+        IInspectable_AddRef( (items[i - start_index] = impl->elements[i]) );
+    }
+    *count = i - start_index;
+
+    return S_OK;
+}
+
+static const struct IVectorView_IInspectableVtbl vector_view_vtbl =
+{
+    vector_view_QueryInterface,
+    vector_view_AddRef,
+    vector_view_Release,
+    /* IInspectable methods */
+    vector_view_GetIids,
+    vector_view_GetRuntimeClassName,
+    vector_view_GetTrustLevel,
+    /* IVectorView<IInspectable*> methods */
+    vector_view_GetAt,
+    vector_view_get_Size,
+    vector_view_IndexOf,
+    vector_view_GetMany,
+};
+
+DEFINE_IINSPECTABLE_( iterable_view, IIterable_IInspectable, struct vector_view, view_impl_from_IIterable_IInspectable,
+                      IIterable_IInspectable_iface, &impl->IVectorView_IInspectable_iface )
+
+static HRESULT WINAPI iterable_view_First( IIterable_IInspectable *iface, IIterator_IInspectable **value )
+{
+    struct vector_view *impl = view_impl_from_IIterable_IInspectable( iface );
+    struct iterator *iter;
+
+    TRACE( "iface %p, value %p.\n", iface, value );
+
+    if (!(iter = calloc( 1, sizeof(struct iterator) ))) return E_OUTOFMEMORY;
+    iter->IIterator_IInspectable_iface.lpVtbl = &iterator_vtbl;
+    iter->iid = impl->iids.iterator;
+    iter->ref = 1;
+
+    IVectorView_IInspectable_AddRef( (iter->view = &impl->IVectorView_IInspectable_iface) );
+    iter->size = impl->size;
+
+    *value = &iter->IIterator_IInspectable_iface;
+    return S_OK;
+}
+
+static const struct IIterable_IInspectableVtbl iterable_view_vtbl =
+{
+    iterable_view_QueryInterface,
+    iterable_view_AddRef,
+    iterable_view_Release,
+    /* IInspectable methods */
+    iterable_view_GetIids,
+    iterable_view_GetRuntimeClassName,
+    iterable_view_GetTrustLevel,
+    /* IIterable<T> methods */
+    iterable_view_First,
+};
+
+struct vector
+{
+    IVector_IInspectable IVector_IInspectable_iface;
+    IIterable_IInspectable IIterable_IInspectable_iface;
+    struct vector_iids iids;
+    LONG ref;
+
+    UINT32 size;
+    UINT32 capacity;
+    IInspectable **elements;
+};
+
+static inline struct vector *impl_from_IVector_IInspectable( IVector_IInspectable *iface )
+{
+    return CONTAINING_RECORD( iface, struct vector, IVector_IInspectable_iface );
+}
+
+static HRESULT WINAPI vector_QueryInterface( IVector_IInspectable *iface, REFIID iid, void **out )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( 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, impl->iids.vector ))
+    {
+        IInspectable_AddRef( (*out = &impl->IVector_IInspectable_iface) );
+        return S_OK;
+    }
+
+    if (IsEqualGUID( iid, impl->iids.iterable ))
+    {
+        IInspectable_AddRef( (*out = &impl->IIterable_IInspectable_iface) );
+        return S_OK;
+    }
+
+    FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI vector_AddRef( IVector_IInspectable *iface )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+    ULONG ref = InterlockedIncrement( &impl->ref );
+    TRACE( "iface %p increasing refcount to %lu.\n", iface, ref );
+    return ref;
+}
+
+static ULONG WINAPI vector_Release( IVector_IInspectable *iface )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+    ULONG ref = InterlockedDecrement( &impl->ref );
+
+    TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref );
+
+    if (!ref)
+    {
+        IVector_IInspectable_Clear( iface );
+        free( impl );
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI vector_GetIids( IVector_IInspectable *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 vector_GetRuntimeClassName( IVector_IInspectable *iface, HSTRING *class_name )
+{
+    FIXME( "iface %p, class_name %p stub!\n", iface, class_name );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI vector_GetTrustLevel( IVector_IInspectable *iface, TrustLevel *trust_level )
+{
+    FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI vector_GetAt( IVector_IInspectable *iface, UINT32 index, IInspectable **value )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+
+    TRACE( "iface %p, index %u, value %p.\n", iface, index, value );
+
+    *value = NULL;
+    if (index >= impl->size) return E_BOUNDS;
+
+    IInspectable_AddRef( (*value = impl->elements[index]) );
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_get_Size( IVector_IInspectable *iface, UINT32 *value )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+    TRACE( "iface %p, value %p.\n", iface, value );
+    *value = impl->size;
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_GetView( IVector_IInspectable *iface, IVectorView_IInspectable **value )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+    struct vector_view *view;
+    ULONG i;
+
+    TRACE( "iface %p, value %p.\n", iface, value );
+
+    if (!(view = calloc( 1, offsetof( struct vector_view, elements[impl->size] ) ))) return E_OUTOFMEMORY;
+    view->IVectorView_IInspectable_iface.lpVtbl = &vector_view_vtbl;
+    view->IIterable_IInspectable_iface.lpVtbl = &iterable_view_vtbl;
+    view->iids = impl->iids;
+    view->ref = 1;
+
+    for (i = 0; i < impl->size; ++i) IInspectable_AddRef( (view->elements[view->size++] = impl->elements[i]) );
+
+    *value = &view->IVectorView_IInspectable_iface;
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_IndexOf( IVector_IInspectable *iface, IInspectable *element, UINT32 *index, BOOLEAN *found )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+    ULONG i;
+
+    TRACE( "iface %p, element %p, index %p, found %p.\n", iface, element, index, found );
+
+    for (i = 0; i < impl->size; ++i) if (impl->elements[i] == element) break;
+    if ((*found = (i < impl->size))) *index = i;
+    else *index = 0;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_SetAt( IVector_IInspectable *iface, UINT32 index, IInspectable *value )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+
+    TRACE( "iface %p, index %u, value %p.\n", iface, index, value );
+
+    if (index >= impl->size) return E_BOUNDS;
+    IInspectable_Release( impl->elements[index] );
+    IInspectable_AddRef( (impl->elements[index] = value) );
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_InsertAt( IVector_IInspectable *iface, UINT32 index, IInspectable *value )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+    IInspectable **tmp = impl->elements;
+
+    TRACE( "iface %p, index %u, value %p.\n", iface, index, value );
+
+    if (impl->size == impl->capacity)
+    {
+        impl->capacity = max( 32, impl->capacity * 3 / 2 );
+        if (!(impl->elements = realloc( impl->elements, impl->capacity * sizeof(*impl->elements) )))
+        {
+            impl->elements = tmp;
+            return E_OUTOFMEMORY;
+        }
+    }
+
+    memmove( impl->elements + index + 1, impl->elements + index, (impl->size++ - index) * sizeof(*impl->elements) );
+    IInspectable_AddRef( (impl->elements[index] = value) );
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_RemoveAt( IVector_IInspectable *iface, UINT32 index )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+
+    TRACE( "iface %p, index %u.\n", iface, index );
+
+    if (index >= impl->size) return E_BOUNDS;
+    IInspectable_Release( impl->elements[index] );
+    memmove( impl->elements + index, impl->elements + index + 1, (--impl->size - index) * sizeof(*impl->elements) );
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_Append( IVector_IInspectable *iface, IInspectable *value )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+
+    TRACE( "iface %p, value %p.\n", iface, value );
+
+    return IVector_IInspectable_InsertAt( iface, impl->size, value );
+}
+
+static HRESULT WINAPI vector_RemoveAtEnd( IVector_IInspectable *iface )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+
+    TRACE( "iface %p.\n", iface );
+
+    if (impl->size) IInspectable_Release( impl->elements[--impl->size] );
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_Clear( IVector_IInspectable *iface )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+
+    TRACE( "iface %p.\n", iface );
+
+    while (impl->size) IVector_IInspectable_RemoveAtEnd( iface );
+    free( impl->elements );
+    impl->capacity = 0;
+    impl->elements = NULL;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_GetMany( IVector_IInspectable *iface, UINT32 start_index,
+                                      UINT32 items_size, IInspectable **items, UINT *count )
+{
+    struct vector *impl = impl_from_IVector_IInspectable( iface );
+    UINT32 i;
+
+    TRACE( "iface %p, start_index %u, items_size %u, items %p, count %p.\n",
+           iface, start_index, items_size, items, count );
+
+    if (start_index >= impl->size) return E_BOUNDS;
+
+    for (i = start_index; i < impl->size; ++i)
+    {
+        if (i - start_index >= items_size) break;
+        IInspectable_AddRef( (items[i - start_index] = impl->elements[i]) );
+    }
+    *count = i - start_index;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI vector_ReplaceAll( IVector_IInspectable *iface, UINT32 count, IInspectable **items )
+{
+    HRESULT hr;
+    ULONG i;
+
+    TRACE( "iface %p, count %u, items %p.\n", iface, count, items );
+
+    hr = IVector_IInspectable_Clear( iface );
+    for (i = 0; i < count && SUCCEEDED(hr); ++i) hr = IVector_IInspectable_Append( iface, items[i] );
+    return hr;
+}
+
+static const struct IVector_IInspectableVtbl vector_vtbl =
+{
+    vector_QueryInterface,
+    vector_AddRef,
+    vector_Release,
+    /* IInspectable methods */
+    vector_GetIids,
+    vector_GetRuntimeClassName,
+    vector_GetTrustLevel,
+    /* IVector<IInspectable*> methods */
+    vector_GetAt,
+    vector_get_Size,
+    vector_GetView,
+    vector_IndexOf,
+    vector_SetAt,
+    vector_InsertAt,
+    vector_RemoveAt,
+    vector_Append,
+    vector_RemoveAtEnd,
+    vector_Clear,
+    vector_GetMany,
+    vector_ReplaceAll,
+};
+
+DEFINE_IINSPECTABLE( iterable, IIterable_IInspectable, struct vector, IVector_IInspectable_iface )
+
+static HRESULT WINAPI iterable_First( IIterable_IInspectable *iface, IIterator_IInspectable **value )
+{
+    struct vector *impl = impl_from_IIterable_IInspectable( iface );
+    IIterable_IInspectable *iterable;
+    IVectorView_IInspectable *view;
+    HRESULT hr;
+
+    TRACE( "iface %p, value %p.\n", iface, value );
+
+    if (FAILED(hr = IVector_IInspectable_GetView( &impl->IVector_IInspectable_iface, &view ))) return hr;
+
+    hr = IVectorView_IInspectable_QueryInterface( view, impl->iids.iterable, (void **)&iterable );
+    IVectorView_IInspectable_Release( view );
+    if (FAILED(hr)) return hr;
+
+    hr = IIterable_IInspectable_First( iterable, value );
+    IIterable_IInspectable_Release( iterable );
+    return hr;
+}
+
+static const struct IIterable_IInspectableVtbl iterable_vtbl =
+{
+    iterable_QueryInterface,
+    iterable_AddRef,
+    iterable_Release,
+    /* IInspectable methods */
+    iterable_GetIids,
+    iterable_GetRuntimeClassName,
+    iterable_GetTrustLevel,
+    /* IIterable<T> methods */
+    iterable_First,
+};
+
+HRESULT vector_create( const struct vector_iids *iids, void **out )
+{
+    struct vector *impl;
+
+    TRACE( "iid %s, out %p.\n", debugstr_guid( iids->vector ), out );
+
+    if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY;
+    impl->IVector_IInspectable_iface.lpVtbl = &vector_vtbl;
+    impl->IIterable_IInspectable_iface.lpVtbl = &iterable_vtbl;
+    impl->iids = *iids;
+    impl->ref = 1;
+
+    *out = &impl->IVector_IInspectable_iface;
+    TRACE( "created %p\n", *out );
+    return S_OK;
+}
diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c
index e7c93116fd1b..4ad83bdba05c 100644
--- a/dlls/winealsa.drv/alsa.c
+++ b/dlls/winealsa.drv/alsa.c
@@ -58,6 +58,7 @@ struct alsa_stream
     AUDCLNT_SHAREMODE share;
     EDataFlow flow;
     HANDLE event;
+    HANDLE timer_thread;
 
     BOOL need_remapping;
     int alsa_channels;
@@ -477,13 +478,6 @@ static WCHAR *alsa_get_card_name(int card)
     return ret;
 }
 
-static NTSTATUS alsa_main_loop(void *args)
-{
-    struct main_loop_params *params = args;
-    NtSetEvent(params->event, NULL);
-    return STATUS_SUCCESS;
-}
-
 static NTSTATUS alsa_get_endpoint_ids(void *args)
 {
     static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
@@ -1031,10 +1025,10 @@ static NTSTATUS alsa_release_stream(void *args)
     struct alsa_stream *stream = handle_get_stream(params->stream);
     SIZE_T size;
 
-    if(params->timer_thread){
+    if(stream->timer_thread){
         stream->please_quit = TRUE;
-        NtWaitForSingleObject(params->timer_thread, FALSE, NULL);
-        NtClose(params->timer_thread);
+        NtWaitForSingleObject(stream->timer_thread, FALSE, NULL);
+        NtClose(stream->timer_thread);
     }
 
     snd_pcm_drop(stream->pcm_handle);
@@ -1521,10 +1515,46 @@ static int alsa_rewind_best_effort(struct alsa_stream *stream)
     return len;
 }
 
+static void alsa_timer_loop(void *args)
+{
+    struct alsa_stream *stream = args;
+    LARGE_INTEGER delay, next;
+    int adjust;
+
+    alsa_lock(stream);
+
+    delay.QuadPart = -stream->mmdev_period_rt;
+    NtQueryPerformanceCounter(&stream->last_period_time, NULL);
+    next.QuadPart = stream->last_period_time.QuadPart + stream->mmdev_period_rt;
+
+    while(!stream->please_quit){
+        if(stream->flow == eRender)
+            alsa_write_data(stream);
+        else if(stream->flow == eCapture)
+            alsa_read_data(stream);
+        alsa_unlock(stream);
+
+        NtDelayExecution(FALSE, &delay);
+
+        alsa_lock(stream);
+        NtQueryPerformanceCounter(&stream->last_period_time, NULL);
+        adjust = next.QuadPart - stream->last_period_time.QuadPart;
+        if(adjust > stream->mmdev_period_rt / 2)
+            adjust = stream->mmdev_period_rt / 2;
+        else if(adjust < -stream->mmdev_period_rt / 2)
+            adjust = -stream->mmdev_period_rt / 2;
+        delay.QuadPart = -(stream->mmdev_period_rt + adjust);
+        next.QuadPart += stream->mmdev_period_rt;
+    }
+
+    alsa_unlock(stream);
+}
+
 static NTSTATUS alsa_start(void *args)
 {
     struct start_params *params = args;
     struct alsa_stream *stream = handle_get_stream(params->stream);
+    static const WCHAR name[] = {'a','u','d','i','o','_','c','l','i','e','n','t','_','t','i','m','e','r',0};
 
     alsa_lock(stream);
 
@@ -1562,6 +1592,7 @@ static NTSTATUS alsa_start(void *args)
             stream->data_in_alsa_frames = 0;
         }
     }
+    if (!stream->timer_thread) create_unix_thread( &stream->timer_thread, name, alsa_timer_loop, stream );
     stream->started = TRUE;
 
     return alsa_unlock_result(stream, &params->result, S_OK);
@@ -1620,44 +1651,6 @@ static NTSTATUS alsa_reset(void *args)
     return alsa_unlock_result(stream, &params->result, S_OK);
 }
 
-static NTSTATUS alsa_timer_loop(void *args)
-{
-    struct timer_loop_params *params = args;
-    struct alsa_stream *stream = handle_get_stream(params->stream);
-    LARGE_INTEGER delay, next;
-    int adjust;
-
-    alsa_lock(stream);
-
-    delay.QuadPart = -stream->mmdev_period_rt;
-    NtQueryPerformanceCounter(&stream->last_period_time, NULL);
-    next.QuadPart = stream->last_period_time.QuadPart + stream->mmdev_period_rt;
-
-    while(!stream->please_quit){
-        if(stream->flow == eRender)
-            alsa_write_data(stream);
-        else if(stream->flow == eCapture)
-            alsa_read_data(stream);
-        alsa_unlock(stream);
-
-        NtDelayExecution(FALSE, &delay);
-
-        alsa_lock(stream);
-        NtQueryPerformanceCounter(&stream->last_period_time, NULL);
-        adjust = next.QuadPart - stream->last_period_time.QuadPart;
-        if(adjust > stream->mmdev_period_rt / 2)
-            adjust = stream->mmdev_period_rt / 2;
-        else if(adjust < -stream->mmdev_period_rt / 2)
-            adjust = -stream->mmdev_period_rt / 2;
-        delay.QuadPart = -(stream->mmdev_period_rt + adjust);
-        next.QuadPart += stream->mmdev_period_rt;
-    }
-
-    alsa_unlock(stream);
-
-    return STATUS_SUCCESS;
-}
-
 static NTSTATUS alsa_get_render_buffer(void *args)
 {
     struct get_render_buffer_params *params = args;
@@ -2410,14 +2403,14 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
 {
     alsa_not_implemented,
     alsa_not_implemented,
-    alsa_main_loop,
+    alsa_not_implemented,
+    alsa_not_implemented,
     alsa_get_endpoint_ids,
     alsa_create_stream,
     alsa_release_stream,
     alsa_start,
     alsa_stop,
     alsa_reset,
-    alsa_timer_loop,
     alsa_get_render_buffer,
     alsa_release_render_buffer,
     alsa_get_capture_buffer,
@@ -2462,19 +2455,6 @@ static NTSTATUS alsa_wow64_process_attach(void *args)
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS alsa_wow64_main_loop(void *args)
-{
-    struct
-    {
-        PTR32 event;
-    } *params32 = args;
-    struct main_loop_params params =
-    {
-        .event = ULongToHandle(params32->event)
-    };
-    return alsa_main_loop(&params);
-}
-
 static NTSTATUS alsa_wow64_get_endpoint_ids(void *args)
 {
     struct
@@ -2539,13 +2519,11 @@ static NTSTATUS alsa_wow64_release_stream(void *args)
     struct
     {
         stream_handle stream;
-        PTR32 timer_thread;
         HRESULT result;
     } *params32 = args;
     struct release_stream_params params =
     {
         .stream = params32->stream,
-        .timer_thread = ULongToHandle(params32->timer_thread)
     };
     alsa_release_stream(&params);
     params32->result = params.result;
@@ -2876,14 +2854,14 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
 {
     alsa_wow64_process_attach,
     alsa_not_implemented,
-    alsa_wow64_main_loop,
+    alsa_not_implemented,
+    alsa_not_implemented,
     alsa_wow64_get_endpoint_ids,
     alsa_wow64_create_stream,
     alsa_wow64_release_stream,
     alsa_start,
     alsa_stop,
     alsa_reset,
-    alsa_timer_loop,
     alsa_wow64_get_render_buffer,
     alsa_release_render_buffer,
     alsa_wow64_get_capture_buffer,
diff --git a/dlls/wineandroid.drv/WineActivity.java b/dlls/wineandroid.drv/WineActivity.java
index 1ee99b8a037e..291e88d5486a 100644
--- a/dlls/wineandroid.drv/WineActivity.java
+++ b/dlls/wineandroid.drv/WineActivity.java
@@ -61,8 +61,7 @@
 
 public class WineActivity extends Activity
 {
-    private native String wine_init( String[] cmdline );
-    private native void wine_looper_init();
+    private native void wine_init();
     public native void wine_desktop_changed( int width, int height );
     public native void wine_config_changed( int dpi );
     public native void wine_surface_changed( int hwnd, Surface surface, boolean opengl );
@@ -138,6 +137,7 @@ private void loadWine( String cmdline )
         File dlldir = new File( libdir, "wine" );
         File prefix = new File( getFilesDir(), "prefix" );
         File loader = new File( dlldir, get_so_dir(wine_abi) + "/wine" );
+        File log = null;
         String locale = Locale.getDefault().getLanguage() + "_" +
             Locale.getDefault().getCountry() + ".UTF-8";
 
@@ -156,7 +156,7 @@ private void loadWine( String cmdline )
         if (winedebug == null) winedebug = readFileString( new File( getFilesDir(), "winedebug" ));
         if (winedebug != null)
         {
-            File log = new File( getFilesDir(), "log" );
+            log = new File( getFilesDir(), "log" );
             putenv( "WINEDEBUG", winedebug );
             putenv( "WINEDEBUGLOG", log.toString() );
             Log.i( LOGTAG, "logging to " + log.toString() );
@@ -165,21 +165,36 @@ private void loadWine( String cmdline )
 
         createProgressDialog( 0, "Setting up the Windows environment..." );
 
-        System.load( dlldir.toString() + get_so_dir(wine_abi) + "/ntdll.so" );
+        for ( String lib : new String[] { "ntdll.so", "win32u.so", "wineandroid.so" } )
+            System.load( dlldir.toString() + get_so_dir(wine_abi) + "/" + lib );
         prefix.mkdirs();
 
-        runWine( loader.toString(), cmdline );
+        runWine( loader.toString(), cmdline, log );
     }
 
-    private final void runWine( String loader, String cmdline )
+    private final void runWine( String loader, String cmdline, File log )
     {
+        CountDownLatch latch = new CountDownLatch(1);
         String[] cmd = { loader,
                          "c:\\windows\\system32\\explorer.exe",
                          "/desktop=shell,,android",
                          cmdline };
 
-        String err = wine_init( cmd );
-        Log.e( LOGTAG, err );
+        runOnUiThread( new Runnable() { public void run() {
+            try { wine_init(); } finally { latch.countDown(); }
+        }});
+        try { latch.await(); } catch ( Exception e ) {}
+
+        try {
+            new ProcessBuilder(cmd)
+                .redirectErrorStream(true)
+                .redirectOutput(ProcessBuilder.Redirect.appendTo(
+                    log != null ? log : new File("/dev/null")
+                ))
+                .start();
+        } catch (IOException e) {
+            Log.e("WineError", "Failed to exec " + String.join(" ", cmd) + ": " + e);
+        }
     }
 
     private void createProgressDialog( final int max, final String message )
@@ -615,11 +630,6 @@ public WineView create_view( boolean is_client )
             if (content_view != null) return content_view;
             content_view = new WineView( WineActivity.this, win, is_client );
             addView( content_view );
-            if (!is_client)
-            {
-                content_view.setFocusable( true );
-                content_view.setFocusableInTouchMode( true );
-            }
             return content_view;
         }
 
@@ -656,8 +666,8 @@ public WineView( Context c, WineWindow win, boolean client )
             setSurfaceTextureListener( this );
             setVisibility( VISIBLE );
             setOpaque( false );
-            setFocusable( true );
-            setFocusableInTouchMode( true );
+            setFocusable( !client );
+            setFocusableInTouchMode( !client );
         }
 
         public WineWindow get_window()
@@ -709,6 +719,16 @@ public boolean onGenericMotionEvent( MotionEvent event )
 
             if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0)
             {
+                /* Primary button press/release is also reported through touch down/up
+                 * on some Android devices. Sending both paths to Wine leaves it with
+                 * duplicate mouse button events, which can desynchronize button state
+                 * and break capture/activation after window moves.
+                 */
+                if ((event.getActionMasked() == MotionEvent.ACTION_BUTTON_PRESS ||
+                     event.getActionMasked() == MotionEvent.ACTION_BUTTON_RELEASE) &&
+                    event.getActionButton() == MotionEvent.BUTTON_PRIMARY)
+                    return true;
+
                 int[] pos = new int[2];
                 window.get_event_pos( event, pos );
                 Log.i( LOGTAG, String.format( "view motion event win %08x action %d pos %d,%d buttons %04x view %d,%d",
@@ -801,6 +821,7 @@ public void create_window( int hwnd, boolean is_desktop, boolean opengl, int par
                 desktop_window = win;
                 top_view.addView( desktop_window.create_whole_view() );
                 desktop_window.client_group.bringToFront();
+                desktop_window.window_group.get_content_view().requestFocus();
             }
         }
         if (opengl) win.create_client_view();
@@ -892,12 +913,4 @@ public void windowPosChanged( final int hwnd, final int flags, final int insert_
             public void run() { window_pos_changed( hwnd, flags, insert_after, owner, style,
                                                     window_rect, client_rect, visible_rect ); }} );
     }
-
-    private void obtainLooper() {
-        CountDownLatch latch = new CountDownLatch(1);
-        runOnUiThread( new Runnable() { public void run() {
-            try { wine_looper_init(); } finally { latch.countDown(); }
-        }});
-        try { latch.await(); } catch ( Exception e ) {}
-    }
 }
diff --git a/dlls/wineandroid.drv/android.h b/dlls/wineandroid.drv/android.h
index c79ce31a0467..cc558aa01f63 100644
--- a/dlls/wineandroid.drv/android.h
+++ b/dlls/wineandroid.drv/android.h
@@ -81,7 +81,7 @@ extern UINT ANDROID_OpenGLInit( UINT version, const struct opengl_funcs *opengl_
  * Android pseudo-device
  */
 
-extern void start_android_device(void);
+extern void createDesktopView( int *event_source );
 extern void register_native_window( HWND hwnd, struct ANativeWindow *win, BOOL client );
 extern struct ANativeWindow *create_ioctl_window( HWND hwnd, BOOL opengl );
 extern struct ANativeWindow *grab_ioctl_window( struct ANativeWindow *window );
@@ -138,6 +138,7 @@ extern void update_keyboard_lock_state( WORD vkey, UINT state );
 
 /* JNI entry points */
 extern void looper_init( JNIEnv *env, jobject obj );
+extern void wine_init_jni( JNIEnv *env, jobject obj );
 extern void desktop_changed( JNIEnv *env, jobject obj, jint width, jint height );
 extern void config_changed( JNIEnv *env, jobject obj, jint dpi );
 extern void surface_changed( JNIEnv *env, jobject obj, jint win, jobject surface,
@@ -195,8 +196,7 @@ union event_data
 
 int send_event( const union event_data *data );
 
-extern JavaVM *java_vm;
-extern jobject java_object;
+extern int event_source;
 
 /* string helpers */
 
diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c
index 34424009afdf..c660c656776a 100644
--- a/dlls/wineandroid.drv/device.c
+++ b/dlls/wineandroid.drv/device.c
@@ -72,6 +72,9 @@ static int log_flags;
 
 WINE_DEFAULT_DEBUG_CHANNEL(android);
 
+static int desktop_client_fd = -1;
+static jobject java_object;
+
 #ifndef SYNC_IOC_WAIT
 #define SYNC_IOC_WAIT _IOW('>', 0, __s32)
 #endif
@@ -79,6 +82,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(android);
 static HWND capture_window;
 static HWND desktop_window;
 
+int event_sink = -1;
 static pthread_mutex_t dispatch_ioctl_lock = PTHREAD_MUTEX_INITIALIZER;
 
 #define ANDROIDCONTROLTYPE  ((ULONG)'A')
@@ -86,6 +90,7 @@ static pthread_mutex_t dispatch_ioctl_lock = PTHREAD_MUTEX_INITIALIZER;
 
 enum android_ioctl
 {
+    IOCTL_CREATE_DESKTOP_VIEW,
     IOCTL_CREATE_WINDOW,
     IOCTL_DESTROY_WINDOW,
     IOCTL_WINDOW_POS_CHANGED,
@@ -147,6 +152,12 @@ struct ioctl_header
     BOOL opengl;
 };
 
+struct ioctl_android_create_desktop_view
+{
+    struct ioctl_header hdr;
+    int                 log_flags;
+};
+
 struct ioctl_android_create_window
 {
     struct ioctl_header hdr;
@@ -467,14 +478,32 @@ static jobject load_java_method( JNIEnv* env, jmethodID *method, const char *nam
     return java_object;
 }
 
-static void create_desktop_view( JNIEnv* env )
+static int createDesktopView_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size, int *reply_fd )
 {
+    static int event_pipe[2];
     static jmethodID method;
     jobject object;
+    struct ioctl_android_create_desktop_view *res = data;
+
+    if (in_size < sizeof(*res)) return -EINVAL;
+
+    if (event_sink != -1) close(event_sink);
+
+    if (pipe2( event_pipe, O_CLOEXEC | O_NONBLOCK ) == -1)
+    {
+        LOG( ERR, "could not create data event pipe\n" );
+        return -1;
+    }
+
+    event_sink = event_pipe[1];
+    *reply_fd = event_pipe[0];
 
-    if (!(object = load_java_method( env, &method, "createDesktopView", "()V" ))) return;
+    log_flags = res->log_flags; /* Copy logging levels from client */
+
+    if (!(object = load_java_method( env, &method, "createDesktopView", "()V" ))) return -ENOSYS;
 
     (*env)->CallVoidMethod( env, object, method );
+    return 0;
 }
 
 static int createWindow_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size, int *reply_fd )
@@ -821,6 +850,7 @@ static int setCursor_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_si
 typedef int (*ioctl_func)( JNIEnv* env, void *in, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size, int *reply_fd );
 static const ioctl_func ioctl_funcs[] =
 {
+    createDesktopView_ioctl,    /* IOCTL_CREATE_DESKTOP_VIEW */
     createWindow_ioctl,         /* IOCTL_CREATE_WINDOW */
     destroyWindow_ioctl,        /* IOCTL_DESTROY_WINDOW */
     windowPosChanged_ioctl,     /* IOCTL_WINDOW_POS_CHANGED */
@@ -838,17 +868,6 @@ static const ioctl_func ioctl_funcs[] =
 static ALooper *looper;
 static JNIEnv *looper_env; /* JNIEnv for the main thread looper. Must only be used from that thread. */
 
-void looper_init( JNIEnv* env, jobject obj )
-{
-    looper_env = env;
-    if (!(looper = pALooper_forThread()))
-    {
-        LOG( ERR, "No looper for current thread\n" );
-        abort();
-    }
-    pALooper_acquire( looper );
-}
-
 /* Handle a single ioctl request from a client socket.
  * Returns 0 if a request was handled successfully and the caller may
  * continue draining the socket, -1 if there is nothing more to read
@@ -883,6 +902,8 @@ static int handle_ioctl_message( JNIEnv *env, int fd )
         {
             pthread_mutex_lock( &dispatch_ioctl_lock );
             status = ioctl_funcs[code]( env, buffer, ret, sizeof(buffer), &reply_size, &reply_fd );
+            if (IOCTL_CREATE_DESKTOP_VIEW == code) /* special case: desktop client */
+                desktop_client_fd = fd;
             pthread_mutex_unlock( &dispatch_ioctl_lock );
         }
     }
@@ -922,6 +943,8 @@ static int looper_handle_client( int fd, int events, void *data )
         {
             pALooper_removeFd( looper, fd );
             close( fd );
+            if (fd == desktop_client_fd) /* our explorer process died */
+                _exit(0);
         }
         break;
     }
@@ -952,25 +975,19 @@ static int looper_handle_listen( int fd, int events, void *data )
     return 1;
 }
 
-static void *bootstrap_looper_thread( void *arg )
+/* main Wine initialisation */
+void wine_init_jni( JNIEnv *env, jobject obj )
 {
-    JNIEnv *env;
-    jmethodID method = NULL;
-    jobject object = NULL;
     int sockfd;
 
-    if (!java_vm || (*java_vm)->AttachCurrentThread( java_vm, &env, 0 ) != JNI_OK)
-    {
-        LOG( ERR,  "Failed to attach current thread\n" );
-        return NULL;
-    }
-
-    if (!(object = load_java_method( env, &method, "obtainLooper", "()V" )))
+    java_object = (*env)->NewGlobalRef( env, obj );
+    looper_env = env;
+    if (!(looper = pALooper_forThread()))
     {
-        LOG( ERR,  "Failed to obtain looper\n" );
+        LOG( ERR, "No looper for current thread\n");
         abort();
     }
-    (*env)->CallVoidMethod( env, object, method );
+    pALooper_acquire( looper );
 
     sockfd = socket( AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0 );
     if (sockfd < 0)
@@ -992,31 +1009,6 @@ static void *bootstrap_looper_thread( void *arg )
         close(sockfd);
         abort();
     }
-
-    create_desktop_view( env );
-
-    (*java_vm)->DetachCurrentThread( java_vm );
-    return NULL;
-}
-
-void start_android_device(void)
-{
-    pthread_t t;
-
-    log_flags = __wine_dbg_get_channel_flags(&__wine_dbch_android);
-
-    /* Use a temporary bootstrap thread to request the main thread looper
-     * without interfering with the current Wine/JVM execution context
-     * (including register and thread-state assumptions). Actual ioctl
-     * dispatch then runs from the main thread looper.
-     */
-    if (!pthread_create( &t, NULL, bootstrap_looper_thread, NULL ))
-        pthread_join(t, NULL);
-    else
-    {
-        LOG( ERR, "Failed to spawn looper bootstrap thread\n" );
-        abort();
-    }
 }
 
 
@@ -1095,6 +1087,13 @@ static void win_decRef( struct android_native_base_t *base )
     InterlockedDecrement( &win->ref );
 }
 
+void createDesktopView( int *event_source )
+{
+    struct ioctl_android_create_desktop_view res = { 0 };
+    res.log_flags = __wine_dbg_get_channel_flags(&__wine_dbch_android);
+    android_ioctl( IOCTL_CREATE_DESKTOP_VIEW, &res, sizeof(res), NULL, NULL, event_source );
+}
+
 static int dequeueBuffer( struct ANativeWindow *window, struct ANativeWindowBuffer **buffer, int *fence )
 {
     struct native_win_wrapper *win = (struct native_win_wrapper *)window;
diff --git a/dlls/wineandroid.drv/init.c b/dlls/wineandroid.drv/init.c
index b5732a12bc1d..df0cb2b72ec8 100644
--- a/dlls/wineandroid.drv/init.c
+++ b/dlls/wineandroid.drv/init.c
@@ -26,6 +26,8 @@
 
 #include "config.h"
 
+#include <fcntl.h>
+#include <unistd.h>
 #include <stdarg.h>
 #include <string.h>
 #include <dlfcn.h>
@@ -39,6 +41,10 @@
 #include "wine/server.h"
 #include "wine/debug.h"
 
+#ifndef WINE_JAVA_CLASS
+#define WINE_JAVA_CLASS "org/winehq/wine/WineActivity"
+#endif
+
 WINE_DEFAULT_DEBUG_CHANNEL(android);
 
 unsigned int screen_width = 0;
@@ -83,7 +89,7 @@ void init_monitors( int width, int height )
            wine_dbgstr_rect( &rect ), wine_dbgstr_rect( &monitor_rc_work ));
 
     /* if we're notified from Java thread, update registry */
-    if (java_vm) NtUserCallNoParam( NtUserCallNoParam_DisplayModeChanged );
+    if (event_source != -1) NtUserCallNoParam( NtUserCallNoParam_DisplayModeChanged );
 }
 
 
@@ -167,7 +173,7 @@ void set_screen_dpi( DWORD dpi )
  */
 static void fetch_display_metrics(void)
 {
-    if (java_vm) return;  /* for Java threads it will be set when the top view is created */
+    if (event_source != -1) return;  /* for Java threads it will be set when the top view is created */
 
     SERVER_START_REQ( get_window_rectangles )
     {
@@ -324,12 +330,12 @@ static const struct user_driver_funcs android_drv_funcs =
 
 static const JNINativeMethod methods[] =
 {
-    { "wine_looper_init", "()V", looper_init },
     { "wine_desktop_changed", "(II)V", desktop_changed },
     { "wine_config_changed", "(I)V", config_changed },
     { "wine_surface_changed", "(ILandroid/view/Surface;Z)V", surface_changed },
     { "wine_motion_event", "(IIIIII)Z", motion_event },
     { "wine_keyboard_event", "(IIII)Z", keyboard_event },
+    { "wine_init", "()V", wine_init_jni }
 };
 
 #define DECL_FUNCPTR(f) typeof(f) * p##f = NULL
@@ -395,8 +401,6 @@ static void load_android_libs(void)
 NTSTATUS __wine_unix_lib_init(void)
 {
     pthread_mutexattr_t attr;
-    jclass class;
-    JNIEnv *jni_env;
 
     load_android_libs();
 
@@ -405,21 +409,20 @@ NTSTATUS __wine_unix_lib_init(void)
     pthread_mutex_init( &win_data_mutex, &attr );
     pthread_mutexattr_destroy( &attr );
 
-    if (java_vm)  /* running under Java */
-    {
-#ifdef __i386__
-        WORD old_fs;
-        __asm__( "mov %%fs,%0" : "=r" (old_fs) );
-#endif
-        (*java_vm)->AttachCurrentThread( java_vm, &jni_env, 0 );
-        class = (*jni_env)->GetObjectClass( jni_env, java_object );
-        (*jni_env)->RegisterNatives( jni_env, class, methods, ARRAY_SIZE( methods ));
-        (*jni_env)->DeleteLocalRef( jni_env, class );
-#ifdef __i386__
-        /* the Java VM hijacks %fs for its own purposes, restore it */
-        __asm__( "mov %0,%%fs" :: "r" (old_fs) );
-#endif
-    }
     __wine_set_user_driver( &android_drv_funcs, WINE_GDI_DRIVER_VERSION );
     return STATUS_SUCCESS;
 }
+
+jint JNI_OnLoad( JavaVM *vm, void *reserved )
+{
+    JNIEnv *env;
+    jclass class;
+
+    load_android_libs();
+
+    if ((*vm)->AttachCurrentThread( vm, &env, NULL ) != JNI_OK) return JNI_ERR;
+    if (!(class = (*env)->FindClass( env, WINE_JAVA_CLASS ))) return JNI_ERR;
+    (*env)->RegisterNatives( env, class, methods, ARRAY_SIZE( methods ));
+    (*env)->DeleteLocalRef( env, class );
+    return JNI_VERSION_1_6;
+}
diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c
index b3f3aeea9556..db9294ee0d1f 100644
--- a/dlls/wineandroid.drv/window.c
+++ b/dlls/wineandroid.drv/window.c
@@ -173,9 +173,11 @@ struct java_event
 
 static struct list event_queue = LIST_INIT( event_queue );
 static struct java_event *current_event;
-static int event_pipe[2];
+int event_source = -1;
 static DWORD desktop_tid;
 
+extern int event_sink;
+
 /***********************************************************************
  *           send_event
  */
@@ -183,7 +185,7 @@ int send_event( const union event_data *data )
 {
     int res;
 
-    if ((res = write( event_pipe[1], data, sizeof(*data) )) != sizeof(*data))
+    if ((res = write( event_sink, data, sizeof(*data) )) != sizeof(*data))
     {
         p__android_log_print( ANDROID_LOG_ERROR, "wine", "failed to send event" );
         return -1;
@@ -343,12 +345,7 @@ static void init_event_queue(void)
     HANDLE handle;
     int ret;
 
-    if (pipe2( event_pipe, O_CLOEXEC | O_NONBLOCK ) == -1)
-    {
-        ERR( "could not create data\n" );
-        NtTerminateProcess( 0, 1 );
-    }
-    if (wine_server_fd_to_handle( event_pipe[0], GENERIC_READ | SYNCHRONIZE, 0, &handle ))
+    if (wine_server_fd_to_handle( event_source, GENERIC_READ | SYNCHRONIZE, 0, &handle ))
     {
         ERR( "Can't allocate handle for event fd\n" );
         NtTerminateProcess( 0, 1 );
@@ -383,7 +380,7 @@ static void pull_events(void)
     {
         if (!(event = malloc( sizeof(*event) ))) break;
 
-        res = read( event_pipe[0], &event->data, sizeof(event->data) );
+        res = read( event_source, &event->data, sizeof(event->data) );
         if (res != sizeof(event->data)) break;
         list_add_tail( &event_queue, &event->entry );
     }
@@ -515,7 +512,7 @@ static int process_events( DWORD mask )
         next = LIST_ENTRY( event_queue.next, struct java_event, entry );
     }
     current_event = previous;
-    return list_empty( &event_queue ) && !check_fd_events( event_pipe[0], POLLIN );
+    return list_empty( &event_queue ) && !check_fd_events( event_source, POLLIN );
 }
 
 
@@ -531,7 +528,7 @@ static int wait_events( int timeout )
         struct pollfd pollfd;
         int ret;
 
-        pollfd.fd = event_pipe[0];
+        pollfd.fd = event_source;
         pollfd.events = POLLIN | POLLHUP;
         ret = poll( &pollfd, 1, timeout );
         if (ret == -1 && errno == EINTR) continue;
@@ -1238,8 +1235,8 @@ BOOL has_client_surface( HWND hwnd )
  */
 BOOL ANDROID_CreateDesktop( const WCHAR *name, UINT width, UINT height )
 {
+    createDesktopView( &event_source );
     init_event_queue();
-    start_android_device();
     /* wait until we receive the surface changed event */
     while (!screen_width)
     {
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c
index 4431f527f73d..ba25d4ee9882 100644
--- a/dlls/winebth.sys/dbus.c
+++ b/dlls/winebth.sys/dbus.c
@@ -32,6 +32,7 @@
 #include <semaphore.h>
 #include <errno.h>
 #include <string.h>
+#include <inttypes.h>
 
 #ifdef SONAME_LIBDBUS_1
 #include <dbus/dbus.h>
@@ -918,8 +919,10 @@ static int named_flag_cmp( const void *flag1, const void *flag2 )
     return strcmp( f1->name, f2->name );
 }
 
-static void
-bluez_gatt_characteristic_props_from_dict_entry( const char *prop_name, DBusMessageIter *variant,
+static BOOL bluez_gatt_characteristic_value_new_from_iter( DBusMessage *message, DBusMessageIter *array_iter,
+                                                           struct winebluetooth_gatt_characteristic_value *value );
+static BOOL
+bluez_gatt_characteristic_props_from_dict_entry( DBusMessage *msg, const char *prop_name, DBusMessageIter *variant,
                                                  struct winebluetooth_watcher_event_gatt_characteristic_added *chrc )
 {
     TRACE_( dbus )( "(%s, %s, %p)\n", debugstr_a( prop_name ), dbgstr_dbus_iter( variant ), chrc );
@@ -959,10 +962,9 @@ bluez_gatt_characteristic_props_from_dict_entry( const char *prop_name, DBusMess
              && p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_OBJECT_PATH)
     {
         const char *path;
-        struct unix_name *service_name;
+
         p_dbus_message_iter_get_basic( variant, &path );
-        service_name = unix_name_get_or_create( path );
-        chrc->service.handle = (UINT_PTR)service_name;
+        return !!(chrc->service.handle = (UINT_PTR)unix_name_get_or_create( path ));
     }
     else if (!strcmp( prop_name, "UUID" )
              && p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_STRING)
@@ -979,6 +981,10 @@ bluez_gatt_characteristic_props_from_dict_entry( const char *prop_name, DBusMess
     else if (!strcmp( prop_name, "Handle" )
              && p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_UINT16)
         p_dbus_message_iter_get_basic( variant, &chrc->props.AttributeHandle );
+    else if (!strcmp( prop_name, "Value" ) && p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_ARRAY
+             && p_dbus_message_iter_get_element_type( variant ) == DBUS_TYPE_BYTE)
+        return bluez_gatt_characteristic_value_new_from_iter( msg, variant, &chrc->value );
+    return TRUE;
 }
 
 static NTSTATUS bluez_adapter_get_props_async( void *connection, const char *radio_object_path,
@@ -1059,8 +1065,11 @@ void bluez_gatt_characteristic_value_free( void *val )
 {
     struct bluez_gatt_characteristic_value *value = val;
 
-    p_dbus_message_unref( value->message );
-    free( value );
+    if (value)
+    {
+        p_dbus_message_unref( value->message );
+        free( value );
+    }
 }
 
 void bluez_gatt_characteristic_value_move( struct winebluetooth_gatt_characteristic_value *value, BYTE *dest )
@@ -1111,7 +1120,7 @@ NTSTATUS bluez_gatt_characteristic_read( void *connection, void *watcher_ctx, st
                                          IRP *irp )
 {
     DBusMessageIter args_iter, dict_iter = DBUS_MESSAGE_ITER_INIT_CLOSED;
-    struct bluez_async_req_data *data = NULL;
+    struct bluez_async_req_data *data;
     DBusPendingCall *pending_call = NULL;
     DBusMessage *request;
     NTSTATUS status;
@@ -1123,14 +1132,7 @@ NTSTATUS bluez_gatt_characteristic_read( void *connection, void *watcher_ctx, st
                                               "ReadValue" );
     if (!request)
         return STATUS_NO_MEMORY;
-    if (!(data = malloc( sizeof( *data ) )))
-    {
-        status = STATUS_NO_MEMORY;
-        goto failed;
-    }
 
-    data->irp = irp;
-    data->watcher_ctx = watcher_ctx;
     p_dbus_message_iter_init_append( request, &args_iter );
     if (!p_dbus_message_iter_open_container( &args_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter ))
     {
@@ -1153,8 +1155,16 @@ NTSTATUS bluez_gatt_characteristic_read( void *connection, void *watcher_ctx, st
         status = STATUS_INTERNAL_ERROR;
         goto failed;
     }
+    if (!(data = malloc( sizeof( *data ) )))
+    {
+        status = STATUS_NO_MEMORY;
+        goto failed;
+    }
+    data->irp = irp;
+    data->watcher_ctx = watcher_ctx;
     if (!p_dbus_pending_call_set_notify( pending_call, bluez_gatt_characteristic_read_callback, data, free ))
     {
+        free( data );
         p_dbus_pending_call_cancel( pending_call );
         p_dbus_pending_call_unref( pending_call );
         status = STATUS_NO_MEMORY;
@@ -1173,16 +1183,6 @@ struct bluez_watcher_ctx
 {
     DBusConnection *connection;
     char *bluez_dbus_unique_name;
-    DBusPendingCall *init_device_list_call;
-
-    /* struct bluez_init_entry */
-    struct list initial_radio_list;
-    /* struct bluez_init_entry */
-    struct list initial_device_list;
-    /* struct bluez_init_entry */
-    struct list initial_gatt_service_list;
-    /* struct bluez_init_entry */
-    struct list initial_gatt_chars_list;
 
     /* struct bluez_watcher_event */
     struct list event_list;
@@ -1222,17 +1222,6 @@ static void bluez_gatt_characteristic_read_callback( DBusPendingCall *pending, v
     p_dbus_message_unref( reply );
 }
 
-struct bluez_init_entry
-{
-    union {
-        struct winebluetooth_watcher_event_radio_added radio;
-        struct winebluetooth_watcher_event_device_added device;
-        struct winebluetooth_watcher_event_gatt_service_added service;
-        struct winebluetooth_watcher_event_gatt_characteristic_added characteristic;
-    } object;
-    struct list entry;
-};
-
 /* The status of a pairing session initiated by BlueZ. */
 enum bluez_pairing_session_status
 {
@@ -1307,17 +1296,42 @@ static NTSTATUS bluez_dbus_get_unique_name_async( DBusConnection *connection, DB
     return success ? (*call ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR) : STATUS_NO_MEMORY;
 }
 
+static NTSTATUS bluez_enumerate_objects( DBusMessage *reply, struct list *event_list );
+
+static void bluez_get_managed_objects_callback( DBusPendingCall *call, void *data )
+{
+    DBusMessage *reply = p_dbus_pending_call_steal_reply( call );
+    struct bluez_watcher_ctx *ctx = data;
+    DBusError error;
+    NTSTATUS status;
+
+    p_dbus_error_init( &error );
+    if (p_dbus_set_error_from_message( &error, reply ))
+        ERR( "Error getting object list from BlueZ: %s\n", dbgstr_dbus_error( &error ) );
+    else if ((status = bluez_enumerate_objects( reply, &ctx->event_list )))
+        ERR( "Error enumerating BlueZ objects: %x\n", status );
+    p_dbus_error_free( &error );
+    p_dbus_message_unref( reply );
+}
+
 /* Called once the BlueZ service has been activated by the system. */
 static void bluez_on_service_available( struct bluez_watcher_ctx *ctx )
 {
+    DBusPendingCall *call;
     NTSTATUS status;
 
     TRACE_(dbus)( "org.bluez available on %s, initializing bluetooth.\n", debugstr_a( ctx->bluez_dbus_unique_name ) );
 
     if ((status = bluez_register_auth_agent_async( ctx->connection )))
         ERR_(dbus)( "Failed to create async RegisterAgent call: %#x\n", status );
-    if ((status = bluez_get_objects_async( ctx->connection, &ctx->init_device_list_call )))
+    if ((status = bluez_get_objects_async( ctx->connection, &call )))
         ERR_(dbus)( "Failed to create async GetManagedObjects call: %#x\n", status );
+    else
+    {
+        if (!p_dbus_pending_call_set_notify( call, bluez_get_managed_objects_callback, ctx, NULL ))
+            p_dbus_pending_call_cancel( call );
+        p_dbus_pending_call_unref( call );
+    }
 }
 
 static void bluez_dbus_get_name_owner_callback( DBusPendingCall *call, void *data )
@@ -1878,6 +1892,223 @@ static UINT16 bluez_dbus_get_invalidated_properties_from_iter(
     return mask;
 }
 
+static void winebluetooth_watcher_event_free( enum winebluetooth_watcher_event_type event_type,
+                                              union winebluetooth_watcher_event_data *event )
+{
+    switch (event_type)
+    {
+    case BLUETOOTH_WATCHER_EVENT_TYPE_SERVICE_DOWN:
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED:
+        if (event->radio_added.radio.handle) unix_name_free( (struct unix_name *)event->radio_added.radio.handle );
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED:
+        if (event->radio_removed.handle) unix_name_free( (struct unix_name *)event->radio_removed.handle );
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED:
+        if (event->radio_props_changed.radio.handle)
+            unix_name_free( (struct unix_name *)event->radio_props_changed.radio.handle );
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED:
+        if (event->device_added.radio.handle) unix_name_free( (struct unix_name *)event->device_added.radio.handle );
+        if (event->device_added.device.handle) unix_name_free( (struct unix_name *)event->device_added.device.handle );
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_REMOVED:
+        if (event->device_removed.device.handle)
+            unix_name_free( (struct unix_name *)event->device_removed.device.handle );
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED:
+        if (event->device_props_changed.device.handle)
+            unix_name_free( (struct unix_name *)event->device_props_changed.device.handle );
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_PAIRING_FINISHED:
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED:
+        if (event->gatt_service_added.device.handle)
+            unix_name_free( (struct unix_name *)event->gatt_service_added.device.handle );
+        if (event->gatt_service_added.service.handle)
+            unix_name_free( (struct unix_name *)event->gatt_service_added.service.handle );
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED:
+        if (event->gatt_service_removed.handle)
+            unix_name_free( (struct unix_name *)event->gatt_service_removed.handle );
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED:
+        if (event->gatt_characteristic_added.characteristic.handle)
+            unix_name_free( (struct unix_name *)event->gatt_characteristic_added.characteristic.handle );
+        if (event->gatt_characteristic_added.service.handle)
+            unix_name_free( (struct unix_name *)event->gatt_characteristic_added.service.handle );
+        bluez_gatt_characteristic_value_free(
+            (struct bluez_gatt_characteristic_value *)event->gatt_characteristic_added.value.handle );
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED:
+        if (event->gatt_characterisic_removed.handle)
+            unix_name_free( (struct unix_name *)event->gatt_characterisic_removed.handle );
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_VALUE_CHANGED:
+        if (event->gatt_characteristic_value_changed.characteristic.handle)
+            unix_name_free( (struct unix_name *)event->gatt_characteristic_value_changed.characteristic.handle );
+        bluez_gatt_characteristic_value_free(
+            (struct bluez_gatt_characteristic_value *)event->gatt_characteristic_added.value.handle );
+        break;
+    case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_VALUE_READ:
+        bluez_gatt_characteristic_value_free(
+            (struct bluez_gatt_characteristic_value *)event->gatt_characteristic_value_read.value.handle );
+        break;
+    }
+}
+
+/* Examine a new BlueZ DBus object available at path and queue a BLUETOOTH_WATCHER_* event if it is an object we
+ * are interested in.
+ * ifaces_iter should point to the start of the interfaces + properties dict.
+ * init_entry should be TRUE if this object has been discovered through initial device discovery in
+ * bluez_enumerate_objects.
+ */
+static BOOL bluez_handle_new_object( DBusMessage *msg, const char *path, DBusMessageIter *ifaces_iter, BOOL init_entry,
+                                     struct list *event_list, BOOL *ret_oom )
+{
+    enum winebluetooth_watcher_event_type event_type = 0;
+    union winebluetooth_watcher_event_data event = {0};
+    BOOL new_object = FALSE, oom = FALSE;
+    DBusMessageIter props_iter;
+    const char *iface;
+
+    TRACE_( dbus )( "(%s, %s, %d, %p, %p)\n", debugstr_a( path ), dbgstr_dbus_iter( ifaces_iter ), init_entry,
+                    event_list, ret_oom );
+
+    while ((iface = bluez_next_dict_entry ( ifaces_iter, &props_iter )))
+    {
+        DBusMessageIter variant;
+        const char *prop_name;
+
+        if (!strcmp( iface, BLUEZ_INTERFACE_ADAPTER ))
+        {
+            if (!(event.radio_added.radio.handle = (UINT_PTR)unix_name_get_or_create( path )))
+            {
+                oom = TRUE;
+                break;
+            }
+            while ((prop_name = bluez_next_dict_entry( &props_iter, &variant )))
+            {
+                bluez_radio_prop_from_dict_entry( prop_name, &variant, &event.radio_added.props,
+                                                  &event.radio_added.props_mask,
+                                                  WINEBLUETOOTH_RADIO_ALL_PROPERTIES );
+            }
+            event_type = BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED;
+            new_object = TRUE;
+            TRACE( "New BlueZ org.bluez.Adapter1 object %s: %#" PRIxPTR "\n", debugstr_a( path ),
+                   event.radio_added.radio.handle );
+        }
+        else if (!strcmp( iface, BLUEZ_INTERFACE_DEVICE ))
+        {
+            if (!(event.device_added.device.handle = (UINT_PTR)unix_name_get_or_create( path )))
+            {
+                oom = TRUE;
+                break;
+            }
+            event.device_added.init_entry = init_entry;
+
+            while ((prop_name = bluez_next_dict_entry( &props_iter, &variant )))
+            {
+                if (!strcmp( prop_name, "Adapter" ) &&
+                    p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_OBJECT_PATH)
+                {
+                    const char *path;
+                    p_dbus_message_iter_get_basic( &variant, &path );
+                    if (!(event.device_added.radio.handle = (UINT_PTR)unix_name_get_or_create( path )))
+                    {
+                        oom = TRUE;
+                        goto done;
+                    }
+                }
+                else
+                    bluez_device_prop_from_dict_entry( prop_name, &variant, &event.device_added.props,
+                                                       &event.device_added.known_props_mask,
+                                                       WINEBLUETOOTH_DEVICE_ALL_PROPERTIES );
+            }
+            if (!event.device_added.radio.handle)
+            {
+                unix_name_free( (struct unix_name *)event.device_added.device.handle );
+                ERR( "Could not find the associated adapter for device %s\n", debugstr_a( path ) );
+            }
+            else
+            {
+                TRACE( "New org.bluez.Device1 object %s: %#" PRIxPTR "\n", debugstr_a( path ),
+                       event.device_added.device.handle );
+                event_type = BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED;
+                new_object = TRUE;
+            }
+        }
+        else if (!strcmp( iface ,BLUEZ_INTERFACE_GATT_SERVICE ))
+        {
+            if (!(event.gatt_service_added.service.handle = (UINT_PTR)unix_name_get_or_create( path )))
+            {
+                oom = TRUE;
+                break;
+            }
+
+            while ((prop_name = bluez_next_dict_entry( &props_iter, &variant )))
+                bluez_gatt_service_props_from_dict_entry( prop_name, &variant, &event.gatt_service_added );
+            if (!event.gatt_service_added.device.handle)
+            {
+                unix_name_free( (struct unix_name *)event.gatt_service_added.service.handle );
+                ERR( "Could not find the associated device for the GATT service %s\n", debugstr_a( path ) );
+            }
+            else
+            {
+                TRACE( "New org.bluez.GattService1 object %s: %#" PRIxPTR "\n", debugstr_a( path ),
+                       event.gatt_service_added.service.handle );
+                event_type = BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED;
+                new_object = TRUE;
+            }
+        }
+        else if (!strcmp( iface, BLUEZ_INTERFACE_GATT_CHARACTERISTICS ))
+        {
+            if (!(event.gatt_characteristic_added.characteristic.handle = (UINT_PTR)unix_name_get_or_create( path )))
+            {
+                oom = TRUE;
+                break;
+            }
+
+            while ((prop_name = bluez_next_dict_entry( &props_iter, &variant )))
+            {
+                if (!bluez_gatt_characteristic_props_from_dict_entry( msg, prop_name, &variant,
+                                                                      &event.gatt_characteristic_added ))
+                {
+                    oom = TRUE;
+                    goto done;
+                }
+            }
+            if (!event.gatt_characteristic_added.service.handle)
+            {
+                unix_name_free( (struct unix_name *)event.gatt_characteristic_added.characteristic.handle );
+                bluez_gatt_characteristic_value_free(
+                    (struct bluez_gatt_characteristic_value *)event.gatt_characteristic_added.value.handle );
+                ERR( "Could not find the associated service for the GATT charcteristic %s\n", debugstr_a( path ) );
+            }
+            else
+            {
+                TRACE( "New org.bluez.GattCharacteristic1 object %s: %#" PRIxPTR "\n", debugstr_a( path ),
+                       event.gatt_characteristic_added.characteristic.handle );
+                event_type = BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED;
+                new_object = TRUE;
+            }
+        }
+
+        if (new_object) break;
+    }
+
+done:
+    if (oom || (new_object && !bluez_event_list_queue_new_event( event_list, event_type, event )))
+    {
+        winebluetooth_watcher_event_free( event_type, &event );
+        if (ret_oom) *ret_oom = TRUE;
+        return FALSE;
+    }
+    if (ret_oom) *ret_oom = FALSE;
+    return new_object;
+}
+
 static void bluez_signal_handler( DBusConnection *conn, DBusMessage *msg, const char *signal_iface,
                                   const char *signal_name, const char *signal_sig, struct bluez_watcher_ctx *ctx )
 {
@@ -1930,179 +2161,8 @@ static void bluez_signal_handler( DBusConnection *conn, DBusMessage *msg, const
         p_dbus_message_iter_get_basic( &iter, &object_path );
         p_dbus_message_iter_next( &iter );
         p_dbus_message_iter_recurse( &iter, &ifaces_iter );
-        while (p_dbus_message_iter_has_next( &ifaces_iter ))
-        {
-            DBusMessageIter iface_entry;
-            const char *iface_name;
-
-            p_dbus_message_iter_recurse( &ifaces_iter, &iface_entry );
-            p_dbus_message_iter_get_basic( &iface_entry, &iface_name );
-            if (!strcmp( iface_name, BLUEZ_INTERFACE_ADAPTER ))
-            {
-                struct winebluetooth_watcher_event_radio_added radio_added = {0};
-                struct unix_name *radio;
-                DBusMessageIter props_iter, variant;
-                const char *prop_name;
-
-                p_dbus_message_iter_next( &iface_entry );
-                p_dbus_message_iter_recurse( &iface_entry, &props_iter );
-
-                while((prop_name = bluez_next_dict_entry( &props_iter, &variant )))
-                {
-                    bluez_radio_prop_from_dict_entry( prop_name, &variant, &radio_added.props,
-                                                      &radio_added.props_mask,
-                                                      WINEBLUETOOTH_RADIO_ALL_PROPERTIES );
-                }
-
-                radio = unix_name_get_or_create( object_path );
-                radio_added.radio.handle = (UINT_PTR)radio;
-                if (!radio_added.radio.handle)
-                {
-                    ERR( "failed to allocate memory for adapter path %s\n", debugstr_a( object_path ) );
-                    break;
-                }
-                else
-                {
-                    union winebluetooth_watcher_event_data event = { .radio_added = radio_added };
-                    TRACE( "New BlueZ org.bluez.Adapter1 object added at %s: %p\n",
-                           debugstr_a( object_path ), radio );
-                    if (!bluez_event_list_queue_new_event(
-                            event_list, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, event ))
-                        unix_name_free( radio );
-                }
-            }
-            else if (!strcmp( iface_name, BLUEZ_INTERFACE_DEVICE ))
-            {
-                struct winebluetooth_watcher_event_device_added device_added = {0};
-                struct unix_name *device_name, *radio_name = NULL;
-                DBusMessageIter props_iter, variant;
-                const char *prop_name;
-
-                device_name = unix_name_get_or_create( object_path );
-                device_added.device.handle = (UINT_PTR)device_name;
-                if (!device_name)
-                {
-                    ERR("Failed to allocate memory for device path %s\n", debugstr_a( object_path ));
-                    break;
-                }
-                p_dbus_message_iter_next( &iface_entry );
-                p_dbus_message_iter_recurse( &iface_entry, &props_iter );
-
-                while((prop_name = bluez_next_dict_entry( &props_iter, &variant )))
-                {
-                    if (!strcmp( prop_name, "Adapter" ) &&
-                        p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_OBJECT_PATH)
-                    {
-                        const char *path;
-
-                        p_dbus_message_iter_get_basic( &variant, &path );
-                        radio_name = unix_name_get_or_create( path );
-                        if (!radio_name)
-                        {
-                            unix_name_free( device_name );
-                            ERR("Failed to allocate memory for radio path %s\n", debugstr_a( path ));
-                            break;
-                        }
-                        device_added.radio.handle = (UINT_PTR)radio_name;
-                    }
-                    else
-                        bluez_device_prop_from_dict_entry( prop_name, &variant, &device_added.props,
-                                                           &device_added.known_props_mask,
-                                                           WINEBLUETOOTH_DEVICE_ALL_PROPERTIES );
-                }
-
-                if (!radio_name)
-                {
-                    unix_name_free( device_name );
-                    ERR( "Could not find the associated adapter for device %s\n", debugstr_a( object_path ) );
-                    break;
-                }
-                else
-                {
-                    union winebluetooth_watcher_event_data event = { .device_added = device_added };
-                    TRACE( "New BlueZ org.bluez.Device1 object added at %s: %p\n", debugstr_a( object_path ),
-                           device_name );
-                    if (!bluez_event_list_queue_new_event( event_list, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED, event ))
-                    {
-                        unix_name_free( device_name );
-                        unix_name_free( radio_name );
-                    }
-                }
-            }
-            else if (!strcmp( iface_name, BLUEZ_INTERFACE_GATT_SERVICE ))
-            {
-                union winebluetooth_watcher_event_data event = {0};
-                DBusMessageIter props_iter, variant;
-                struct unix_name *service_name;
-                const char *prop_name;
-
-                if (!(service_name = unix_name_get_or_create( object_path )))
-                {
-                    ERR( "Failed to allocate memory for GATT service path %s\n", debugstr_a( object_path ) );
-                    break;
-                }
-                event.gatt_service_added.service.handle = (UINT_PTR)service_name;
-                p_dbus_message_iter_next( &iface_entry );
-                p_dbus_message_iter_recurse( &iface_entry, &props_iter );
-
-                while ((prop_name = bluez_next_dict_entry( &props_iter, &variant )))
-                    bluez_gatt_service_props_from_dict_entry( prop_name, &variant, &event.gatt_service_added );
-
-                if (!event.gatt_service_added.device.handle)
-                {
-                    unix_name_free( service_name );
-                    ERR( "Could not find the associated device for the GATT service %s\n", debugstr_a( object_path ) );
-                    break;
-                }
-
-                TRACE( "New Bluez org.bluez.GattService1 object added at %s: %p\n", debugstr_a( object_path ),
-                       service_name );
-                if (!bluez_event_list_queue_new_event( event_list, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED,
-                                                       event ))
-                {
-                    unix_name_free( service_name );
-                    unix_name_free( (struct unix_name *)event.gatt_service_added.device.handle );
-                }
-            }
-            else if (!strcmp( iface_name, BLUEZ_INTERFACE_GATT_CHARACTERISTICS ))
-            {
-                struct winebluetooth_watcher_event_gatt_characteristic_added chrc_added = {0};
-                union winebluetooth_watcher_event_data event = {0};
-                struct unix_name *chrc_name;
-                DBusMessageIter props_iter, variant;
-                const char *prop_name;
-
-                chrc_name = unix_name_get_or_create( object_path );
-                chrc_added.characteristic.handle = (UINT_PTR)chrc_name;
-                if (!chrc_name)
-                {
-                    ERR( "Failed to allocate memory for GATT characteristic path %s\n", debugstr_a( object_path ) );
-                    break;
-                }
-                p_dbus_message_iter_next( &iface_entry );
-                p_dbus_message_iter_recurse( &iface_entry, &props_iter );
 
-                while ((prop_name = bluez_next_dict_entry( &props_iter, &variant )))
-                    bluez_gatt_characteristic_props_from_dict_entry( prop_name, &variant, &chrc_added );
-                if (!chrc_added.service.handle)
-                {
-                    unix_name_free( chrc_name );
-                    ERR( "Could not find the associated service for the GATT characteristic %s\n",
-                         debugstr_a( object_path ) );
-                    break;
-                }
-                event.gatt_characteristic_added = chrc_added;
-                TRACE( "New BlueZ org.bluez.GattCharacterisic1 object added at %s: %p\n", debugstr_a( object_path ),
-                       object_path );
-                if (!bluez_event_list_queue_new_event( event_list, BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED,
-                                                       event ))
-                {
-                    unix_name_free( chrc_name );
-                    unix_name_free( (struct unix_name *)chrc_added.service.handle );
-                }
-            }
-            p_dbus_message_iter_next( &ifaces_iter );
-        }
+        bluez_handle_new_object( msg, object_path, &ifaces_iter, FALSE, event_list, NULL );
     }
     else if (!strcmp( signal_iface, DBUS_INTERFACE_OBJECTMANAGER ) &&
              !strcmp( signal_name, DBUS_OBJECTMANAGER_SIGNAL_INTERFACESREMOVED ) &&
@@ -2475,74 +2535,15 @@ static const char *BLUEZ_MATCH_RULES[] = { BLUEZ_MATCH_OBJECTMANAGER, BLUEZ_MATC
 static void bluez_watcher_free( struct bluez_watcher_ctx *watcher )
 {
     struct bluez_watcher_event *event1, *event2;
-    struct bluez_init_entry *entry1, *entry2;
-
-    if (watcher->init_device_list_call)
-    {
-        p_dbus_pending_call_cancel( watcher->init_device_list_call );
-        p_dbus_pending_call_unref( watcher->init_device_list_call );
-    }
-
-    LIST_FOR_EACH_ENTRY_SAFE( entry1, entry2, &watcher->initial_radio_list, struct bluez_init_entry, entry )
-    {
-        list_remove( &entry1->entry );
-        unix_name_free( (struct unix_name *)entry1->object.radio.radio.handle );
-        free( entry1 );
-    }
 
     LIST_FOR_EACH_ENTRY_SAFE( event1, event2, &watcher->event_list, struct bluez_watcher_event, entry )
     {
         list_remove( &event1->entry );
-        switch (event1->event_type)
+        winebluetooth_watcher_event_free( event1->event_type, &event1->event );
+        if (event1->pending_call)
         {
-        case BLUETOOTH_WATCHER_EVENT_TYPE_SERVICE_DOWN:
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED:
-            unix_name_free( (struct unix_name *)event1->event.radio_added.radio.handle );
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED:
-            unix_name_free( (struct unix_name *)event1->event.radio_removed.handle );
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED:
-            unix_name_free( (struct unix_name *)event1->event.radio_props_changed.radio.handle );
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED:
-            unix_name_free( (struct unix_name *)event1->event.device_added.radio.handle );
-            unix_name_free( (struct unix_name *)event1->event.device_added.device.handle );
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_REMOVED:
-            unix_name_free( (struct unix_name *)event1->event.device_removed.device.handle );
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED:
-            unix_name_free( (struct unix_name *)event1->event.device_props_changed.device.handle );
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_PAIRING_FINISHED:
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED:
-            unix_name_free( (struct unix_name *)event1->event.gatt_service_added.device.handle );
-            unix_name_free( (struct unix_name *)event1->event.gatt_service_added.service.handle );
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED:
-            unix_name_free( (struct unix_name *)event1->event.gatt_service_removed.handle );
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED:
-            unix_name_free( (struct unix_name *)event1->event.gatt_characteristic_added.characteristic.handle );
-            unix_name_free( (struct unix_name *)event1->event.gatt_characteristic_added.service.handle );
-            bluez_gatt_characteristic_value_free(
-                (struct bluez_gatt_characteristic_value *)event1->event.gatt_characteristic_added.value.handle );
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED:
-            unix_name_free( (struct unix_name *)event1->event.gatt_characterisic_removed.handle );
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_VALUE_CHANGED:
-            unix_name_free( (struct unix_name *)event1->event.gatt_characteristic_value_changed.characteristic.handle );
-            bluez_gatt_characteristic_value_free(
-                (struct bluez_gatt_characteristic_value *)event1->event.gatt_characteristic_added.value.handle );
-            break;
-        case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_VALUE_READ:
-            bluez_gatt_characteristic_value_free(
-                (struct bluez_gatt_characteristic_value *)event1->event.gatt_characteristic_value_read.value.handle );
-            break;
+            p_dbus_pending_call_cancel( event1->pending_call );
+            p_dbus_pending_call_unref( event1->pending_call );
         }
         free( event1 );
     }
@@ -2562,10 +2563,6 @@ NTSTATUS bluez_watcher_init( void *connection, void **ctx )
 
     if (!(watcher_ctx = calloc( 1, sizeof( struct bluez_watcher_ctx ) ))) return STATUS_NO_MEMORY;
     watcher_ctx->connection = p_dbus_connection_ref( connection );
-    list_init( &watcher_ctx->initial_radio_list );
-    list_init( &watcher_ctx->initial_device_list );
-    list_init( &watcher_ctx->initial_gatt_service_list );
-    list_init( &watcher_ctx->initial_gatt_chars_list );
     list_init( &watcher_ctx->event_list );
 
     /* The bluez_dbus_loop thread will free up the watcher when the disconnect message is processed (i.e,
@@ -2604,11 +2601,6 @@ done:
     p_dbus_error_free( &err );
     if (status)
     {
-        if (watcher_ctx->init_device_list_call)
-        {
-            p_dbus_pending_call_cancel( watcher_ctx->init_device_list_call );
-            p_dbus_pending_call_unref( watcher_ctx->init_device_list_call );
-        }
         p_dbus_connection_unref( watcher_ctx->connection );
         free( watcher_ctx->bluez_dbus_unique_name );
         free( watcher_ctx );
@@ -2637,13 +2629,12 @@ void bluez_watcher_close( void *connection, void *ctx )
     p_dbus_connection_remove_filter( connection, bluez_filter, ctx );
 }
 
-static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct list *adapter_list,
-                                                  struct list *device_list, struct list *gatt_service_list,
-                                                  struct list *gatt_chars_list )
+/* Enuerate through the list of BlueZ objects contained in reply (from GetManagedObjects) and queue the associated
+ * BLUETOOTH_WATCHER_ event for objects we're interested in onto event_list. */
+static NTSTATUS bluez_enumerate_objects( DBusMessage *reply, struct list *event_list )
 {
-    DBusMessageIter dict, paths_iter, iface_iter, prop_iter;
+    DBusMessageIter dict, paths_iter, iface_iter;
     const char *path;
-    NTSTATUS status = STATUS_SUCCESS;
 
     if (!p_dbus_message_has_signature( reply,
                                        DBUS_OBJECTMANAGER_METHOD_GETMANAGEDOBJECTS_RETURN_SIGNATURE ))
@@ -2657,259 +2648,28 @@ static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct lis
     p_dbus_message_iter_recurse( &dict, &paths_iter );
     while((path = bluez_next_dict_entry( &paths_iter, &iface_iter )))
     {
-        const char *iface;
-        while ((iface = bluez_next_dict_entry ( &iface_iter, &prop_iter )))
-        {
-            if (!strcmp( iface, BLUEZ_INTERFACE_ADAPTER ))
-            {
-                const char *prop_name;
-                DBusMessageIter variant;
-                struct bluez_init_entry *init_device = calloc( 1, sizeof( *init_device ) );
-                struct unix_name *radio_name;
-
-                if (!init_device)
-                {
-                    status = STATUS_NO_MEMORY;
-                    goto done;
-                }
-                radio_name = unix_name_get_or_create( path );
-                if (!radio_name)
-                {
-                    free( init_device );
-                    status = STATUS_NO_MEMORY;
-                    goto done;
-                }
-                while ((prop_name = bluez_next_dict_entry( &prop_iter, &variant )))
-                {
-                    bluez_radio_prop_from_dict_entry(
-                        prop_name, &variant, &init_device->object.radio.props,
-                        &init_device->object.radio.props_mask, WINEBLUETOOTH_RADIO_ALL_PROPERTIES );
-                }
-                init_device->object.radio.radio.handle = (UINT_PTR)radio_name;
-                list_add_tail( adapter_list, &init_device->entry );
-                TRACE( "Found BlueZ org.bluez.Adapter1 object %s: %p\n",
-                       debugstr_a( radio_name->str ), radio_name );
-                break;
-            }
-            else if (!strcmp( iface, BLUEZ_INTERFACE_DEVICE ))
-            {
-                const char *prop_name;
-                DBusMessageIter variant;
-                struct bluez_init_entry *init_device;
-                struct unix_name *device_name, *radio_name = NULL;
-
-                init_device = calloc( 1, sizeof( *init_device ) );
-                if (!init_device)
-                {
-                    status = STATUS_NO_MEMORY;
-                    goto done;
-                }
-                device_name = unix_name_get_or_create( path );
-                if (!device_name)
-                {
-                    free( init_device );
-                    status = STATUS_NO_MEMORY;
-                    goto done;
-                }
-                init_device->object.device.device.handle = (UINT_PTR)device_name;
-                init_device->object.device.init_entry = TRUE;
-
-                while((prop_name = bluez_next_dict_entry( &prop_iter, &variant )))
-                {
-                    if (!strcmp( prop_name, "Adapter" ) &&
-                        p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_OBJECT_PATH)
-                    {
-                        const char *path;
-                        p_dbus_message_iter_get_basic( &variant, &path );
-                        radio_name = unix_name_get_or_create( path );
-                        if (!radio_name)
-                        {
-                            unix_name_free( device_name );
-                            free( init_device );
-                            status = STATUS_NO_MEMORY;
-                            goto done;
-                        }
-                        init_device->object.device.radio.handle = (UINT_PTR)radio_name;
-                    }
-                    else
-                        bluez_device_prop_from_dict_entry( prop_name, &variant, &init_device->object.device.props,
-                                                           &init_device->object.device.known_props_mask,
-                                                           WINEBLUETOOTH_DEVICE_ALL_PROPERTIES );
-                }
-                if (!init_device->object.device.radio.handle)
-                {
-                    unix_name_free( device_name );
-                    free( init_device );
-                    ERR( "Could not find the associated adapter for device %s\n", debugstr_a( path ) );
-                    break;
-                }
-                list_add_tail( device_list, &init_device->entry );
-                TRACE( "Found BlueZ org.bluez.Device1 object %s: %p\n", debugstr_a( path ), device_name );
-                break;
-            }
-            else if (!strcmp( iface, BLUEZ_INTERFACE_GATT_SERVICE ))
-            {
-                struct bluez_init_entry *init_device;
-                struct unix_name *service_name;
-                DBusMessageIter variant;
-                const char *prop_name;
-
-                init_device = calloc( 1, sizeof( *init_device ) );
-                if (!init_device)
-                {
-                    status = STATUS_NO_MEMORY;
-                    goto done;
-                }
-
-                service_name = unix_name_get_or_create( path );
-                if (!service_name)
-                {
-                    ERR( "Failed to allocate memory for service path %s\n", debugstr_a( path ) );
-                    free( init_device );
-                    break;
-                }
-                init_device->object.service.service.handle = (UINT_PTR)service_name;
-
-                while ((prop_name = bluez_next_dict_entry( &prop_iter, &variant )))
-                    bluez_gatt_service_props_from_dict_entry( prop_name, &variant, &init_device->object.service );
-                if (!init_device->object.service.device.handle)
-                {
-                    unix_name_free( service_name );
-                    free( init_device );
-                    ERR( "Could not find the associated device for the GATT service %s\n", debugstr_a( path ) );
-                    break;
-                }
-                list_add_tail( gatt_service_list, &init_device->entry );
-                TRACE( "Found BlueZ org.bluez.GattService1 object %s %p\n", debugstr_a( path ), service_name );
-                break;
-            }
-            else if (!strcmp( iface, BLUEZ_INTERFACE_GATT_CHARACTERISTICS ))
-            {
-                struct unix_name *char_name;
-                struct bluez_init_entry *init_entry;
-                DBusMessageIter variant;
-                const char *prop_name;
-
-                init_entry = calloc( 1, sizeof( *init_entry ) );
-                if (!init_entry)
-                {
-                    status = STATUS_NO_MEMORY;
-                    goto done;
-                }
-                char_name = unix_name_get_or_create( path );
-                if (!char_name)
-                {
-                    ERR("Failed to allocate memory for characteristic path %s\n", debugstr_a( path ));
-                    free( init_entry );
-                    goto done;
-                }
-
-                init_entry->object.characteristic.characteristic.handle = (UINT_PTR)char_name;
-                while ((prop_name = bluez_next_dict_entry( &prop_iter, &variant )))
-                {
-                    if (!strcmp( prop_name, "Value" )
-                        && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_ARRAY
-                        && p_dbus_message_iter_get_element_type( &variant ) == DBUS_TYPE_BYTE)
-                    {
-                        if (!bluez_gatt_characteristic_value_new_from_iter( reply, &variant,
-                                                                            &init_entry->object.characteristic.value ))
-                        {
-                            unix_name_free( char_name );
-                            free( init_entry );
-                            status = STATUS_NO_MEMORY;
-                            goto done;
-                        }
-                    }
-                    else
-                        bluez_gatt_characteristic_props_from_dict_entry( prop_name, &variant,
-                                                                         &init_entry->object.characteristic );
-                }
-                if (!init_entry->object.characteristic.service.handle)
-                {
-                    bluez_gatt_characteristic_value_free(
-                        (struct bluez_gatt_characteristic_value *)init_entry->object.characteristic.value.handle );
-                    unix_name_free( char_name );
-                    free( init_entry );
-                    ERR( "Could not find the associated service for the GATT charcteristic %s\n", debugstr_a( path ) );
-                    break;
-                }
-
-                list_add_tail( gatt_chars_list, &init_entry->entry );
-                TRACE( "Found Bluez org.bluez.GattCharacteristic1 object %s %p\n", debugstr_a( path ), char_name );
-                break;
-            }
-        }
+        BOOL oom;
+        if (!bluez_handle_new_object( reply, path, &iface_iter, TRUE, event_list, &oom ) && oom)
+            return STATUS_NO_MEMORY;
     }
-
-    TRACE( "Initial device list: radios: %d, devices: %d, GATT services: %d, characteristics: %d \n",
-           list_count( adapter_list ), list_count( device_list ), list_count( gatt_service_list ),
-           list_count( gatt_chars_list ) );
- done:
-    return status;
+    return STATUS_SUCCESS;
 }
 
-static BOOL bluez_watcher_event_queue_ready( struct bluez_watcher_ctx *ctx, struct winebluetooth_watcher_event *event )
+static BOOL bluez_watcher_event_queue_pop( struct bluez_watcher_ctx *ctx, struct winebluetooth_watcher_event *event )
 {
-    if (!list_empty( &ctx->initial_radio_list ))
-    {
-        struct bluez_init_entry *radio;
-
-        radio = LIST_ENTRY( list_head( &ctx->initial_radio_list ), struct bluez_init_entry, entry );
-        event->event_type = BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED;
-        event->event_data.radio_added = radio->object.radio;
-        list_remove( &radio->entry );
-        free( radio );
-        return TRUE;
-    }
-    if (!list_empty( &ctx->initial_device_list ))
-    {
-        struct bluez_init_entry *device;
-
-        device = LIST_ENTRY( list_head( &ctx->initial_device_list ), struct bluez_init_entry, entry );
-        event->event_type = BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED;
-        event->event_data.device_added = device->object.device;
-        list_remove( &device->entry );
-        free( device );
-        return TRUE;
-    }
-    if (!list_empty( &ctx->initial_gatt_service_list ))
-    {
-        struct bluez_init_entry *service;
+    struct bluez_watcher_event *head;
 
-        service = LIST_ENTRY( list_head( &ctx->initial_gatt_service_list ), struct bluez_init_entry, entry );
-        event->event_type = BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED;
-        event->event_data.gatt_service_added = service->object.service;
-        list_remove( &service->entry );
-        free( service );
-        return TRUE;
-    }
-    if (!list_empty( &ctx->initial_gatt_chars_list ))
-    {
-        struct bluez_init_entry *characteristic;
-        characteristic = LIST_ENTRY( list_head( &ctx->initial_gatt_chars_list ), struct bluez_init_entry, entry );
-        event->event_type = BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED;
-        event->event_data.gatt_characteristic_added = characteristic->object.characteristic;
-        list_remove( &characteristic->entry );
-        free( characteristic );
-        return TRUE;
-    }
-    if (!list_empty( &ctx->event_list ))
-    {
-        struct bluez_watcher_event *watcher_event =
-            LIST_ENTRY( list_head( &ctx->event_list ), struct bluez_watcher_event, entry );
+    if (list_empty( &ctx->event_list )) return FALSE;
 
-        if (watcher_event->pending_call && !p_dbus_pending_call_get_completed( watcher_event->pending_call ))
-            return FALSE;
+    head = LIST_ENTRY( list_head( &ctx->event_list ), struct bluez_watcher_event, entry );
+    if (head->pending_call && !p_dbus_pending_call_get_completed( head->pending_call )) return FALSE;
 
-        event->event_type = watcher_event->event_type;
-        event->event_data = watcher_event->event;
-        list_remove( &watcher_event->entry );
-        if (watcher_event->pending_call)
-            p_dbus_pending_call_unref( watcher_event->pending_call );
-        free( watcher_event );
-        return TRUE;
-    }
-    return FALSE;
+    event->event_type = head->event_type;
+    event->event_data = head->event;
+    list_remove( &head->entry );
+    if (head->pending_call) p_dbus_pending_call_unref( head->pending_call );
+    free( head );
+    return TRUE;
 }
 
 static BOOL bluez_auth_agent_ctx_have_event( struct bluez_auth_agent_ctx *ctx,
@@ -2944,7 +2704,7 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, void *auth_agent,
 
     while (TRUE)
     {
-        if (bluez_watcher_event_queue_ready( watcher_ctx, &result->data.watcher_event ))
+        if (bluez_watcher_event_queue_pop( watcher_ctx, &result->data.watcher_event ))
         {
             result->status = WINEBLUETOOTH_EVENT_WATCHER_EVENT;
             p_dbus_connection_unref( connection );
@@ -2966,40 +2726,6 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, void *auth_agent,
             TRACE( "Disconnected from DBus\n" );
             return STATUS_SUCCESS;
         }
-
-        if (watcher_ctx->init_device_list_call != NULL
-            && p_dbus_pending_call_get_completed( watcher_ctx->init_device_list_call ))
-        {
-            DBusMessage *reply = p_dbus_pending_call_steal_reply( watcher_ctx->init_device_list_call );
-            DBusError error;
-            NTSTATUS status;
-
-            p_dbus_pending_call_unref( watcher_ctx->init_device_list_call );
-            watcher_ctx->init_device_list_call = NULL;
-
-            p_dbus_error_init( &error );
-            if (p_dbus_set_error_from_message( &error, reply ))
-            {
-                WARN( "Error getting object list from BlueZ: %s\n", dbgstr_dbus_error( &error ) );
-                p_dbus_error_free( &error );
-                p_dbus_message_unref( reply );
-                p_dbus_connection_unref( connection );
-                bluez_auth_agent_ctx_decref( auth_agent );
-                return STATUS_NO_MEMORY;
-            }
-            status = bluez_build_initial_device_lists( reply, &watcher_ctx->initial_radio_list,
-                                                       &watcher_ctx->initial_device_list,
-                                                       &watcher_ctx->initial_gatt_service_list,
-                                                       &watcher_ctx->initial_gatt_chars_list );
-            p_dbus_message_unref( reply );
-            if (status != STATUS_SUCCESS)
-            {
-                WARN( "Error building initial bluetooth devices list: %#x\n", status );
-                p_dbus_connection_unref( connection );
-                bluez_auth_agent_ctx_decref( auth_agent );
-                return status;
-            }
-        }
     }
 }
 #else
diff --git a/dlls/winecoreaudio.drv/coreaudio.c b/dlls/winecoreaudio.drv/coreaudio.c
index a4f78f6d6bb9..c7e0dc240485 100644
--- a/dlls/winecoreaudio.drv/coreaudio.c
+++ b/dlls/winecoreaudio.drv/coreaudio.c
@@ -87,6 +87,7 @@ struct coreaudio_stream
     DWORD flags;
     AUDCLNT_SHAREMODE share;
     HANDLE event;
+    HANDLE timer_thread;
 
     BOOL playing, please_quit;
     REFERENCE_TIME period;
@@ -199,13 +200,6 @@ static BOOL device_has_channels(AudioDeviceID device, EDataFlow flow)
     return ret;
 }
 
-static NTSTATUS unix_main_loop(void *args)
-{
-    struct main_loop_params *params = args;
-    NtSetEvent(params->event, NULL);
-    return STATUS_SUCCESS;
-}
-
 static NTSTATUS unix_get_endpoint_ids(void *args)
 {
     struct get_endpoint_ids_params *params = args;
@@ -817,10 +811,10 @@ static NTSTATUS unix_release_stream( void *args )
     struct coreaudio_stream *stream = handle_get_stream(params->stream);
     SIZE_T size;
 
-    if(params->timer_thread){
+    if(stream->timer_thread){
         stream->please_quit = TRUE;
-        NtWaitForSingleObject(params->timer_thread, FALSE, NULL);
-        NtClose(params->timer_thread);
+        NtWaitForSingleObject(stream->timer_thread, FALSE, NULL);
+        NtClose(stream->timer_thread);
     }
 
     if(stream->unit){
@@ -1348,10 +1342,37 @@ static NTSTATUS unix_get_current_padding(void *args)
     return STATUS_SUCCESS;
 }
 
+static void unix_timer_loop(void *args)
+{
+    struct coreaudio_stream *stream = args;
+    LARGE_INTEGER delay, next, last;
+    int adjust;
+
+    delay.QuadPart = -stream->period;
+    NtQueryPerformanceCounter(&last, NULL);
+    next.QuadPart = last.QuadPart + stream->period;
+
+    while(!stream->please_quit){
+        NtSetEvent(stream->event, NULL);
+        NtDelayExecution(FALSE, &delay);
+        NtQueryPerformanceCounter(&last, NULL);
+
+        adjust = next.QuadPart - last.QuadPart;
+        if(adjust > stream->period / 2)
+            adjust = stream->period / 2;
+        else if(adjust < -stream->period / 2)
+            adjust = -stream->period / 2;
+
+        delay.QuadPart = -(stream->period + adjust);
+        next.QuadPart += stream->period;
+    }
+}
+
 static NTSTATUS unix_start(void *args)
 {
     struct start_params *params = args;
     struct coreaudio_stream *stream = handle_get_stream(params->stream);
+    static const WCHAR name[] = {'a','u','d','i','o','_','c','l','i','e','n','t','_','t','i','m','e','r',0};
 
     os_unfair_lock_lock(&stream->lock);
 
@@ -1365,6 +1386,7 @@ static NTSTATUS unix_start(void *args)
     }
 
     os_unfair_lock_unlock(&stream->lock);
+    if (!stream->timer_thread) create_unix_thread( &stream->timer_thread, name, unix_timer_loop, stream );
 
     return STATUS_SUCCESS;
 }
@@ -1416,35 +1438,6 @@ static NTSTATUS unix_reset(void *args)
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS unix_timer_loop(void *args)
-{
-    struct timer_loop_params *params = args;
-    struct coreaudio_stream *stream = handle_get_stream(params->stream);
-    LARGE_INTEGER delay, next, last;
-    int adjust;
-
-    delay.QuadPart = -stream->period;
-    NtQueryPerformanceCounter(&last, NULL);
-    next.QuadPart = last.QuadPart + stream->period;
-
-    while(!stream->please_quit){
-        NtSetEvent(stream->event, NULL);
-        NtDelayExecution(FALSE, &delay);
-        NtQueryPerformanceCounter(&last, NULL);
-
-        adjust = next.QuadPart - last.QuadPart;
-        if(adjust > stream->period / 2)
-            adjust = stream->period / 2;
-        else if(adjust < -stream->period / 2)
-            adjust = -stream->period / 2;
-
-        delay.QuadPart = -(stream->period + adjust);
-        next.QuadPart += stream->period;
-    }
-
-    return STATUS_SUCCESS;
-}
-
 static NTSTATUS unix_get_render_buffer(void *args)
 {
     struct get_render_buffer_params *params = args;
@@ -1774,14 +1767,14 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
 {
     unix_not_implemented,
     unix_not_implemented,
-    unix_main_loop,
+    unix_not_implemented,
+    unix_not_implemented,
     unix_get_endpoint_ids,
     unix_create_stream,
     unix_release_stream,
     unix_start,
     unix_stop,
     unix_reset,
-    unix_timer_loop,
     unix_get_render_buffer,
     unix_release_render_buffer,
     unix_get_capture_buffer,
@@ -1826,19 +1819,6 @@ static NTSTATUS unix_wow64_process_attach(void *args)
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS unix_wow64_main_loop(void *args)
-{
-    struct
-    {
-        PTR32 event;
-    } *params32 = args;
-    struct main_loop_params params =
-    {
-        .event = ULongToHandle(params32->event)
-    };
-    return unix_main_loop(&params);
-}
-
 static NTSTATUS unix_wow64_get_endpoint_ids(void *args)
 {
     struct
@@ -1903,13 +1883,11 @@ static NTSTATUS unix_wow64_release_stream(void *args)
     struct
     {
         stream_handle stream;
-        PTR32 timer_thread;
         HRESULT result;
     } *params32 = args;
     struct release_stream_params params =
     {
         .stream = params32->stream,
-        .timer_thread = ULongToHandle(params32->timer_thread)
     };
     unix_release_stream(&params);
     params32->result = params.result;
@@ -2239,14 +2217,14 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
 {
     unix_wow64_process_attach,
     unix_not_implemented,
-    unix_wow64_main_loop,
+    unix_not_implemented,
+    unix_not_implemented,
     unix_wow64_get_endpoint_ids,
     unix_wow64_create_stream,
     unix_wow64_release_stream,
     unix_start,
     unix_stop,
     unix_reset,
-    unix_timer_loop,
     unix_wow64_get_render_buffer,
     unix_release_render_buffer,
     unix_wow64_get_capture_buffer,
diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
index 85eab01d711a..19ec8070dd9d 100644
--- a/dlls/wined3d/directx.c
+++ b/dlls/wined3d/directx.c
@@ -658,7 +658,8 @@ static const struct wined3d_gpu_description gpu_description_table[] =
     {HW_VENDOR_INTEL,      CARD_INTEL_IPP580_1,            "Intel(R) Iris(TM) Pro Graphics P580",                       DRIVER_INTEL_HD4000,  2048},
     {HW_VENDOR_INTEL,      CARD_INTEL_IPP580_2,            "Intel(R) Iris(TM) Pro Graphics P580",                       DRIVER_INTEL_HD4000,  2048},
     {HW_VENDOR_INTEL,      CARD_INTEL_UHD617,              "Intel(R) UHD Graphics 617",                                 DRIVER_INTEL_HD4000,  2048},
-    {HW_VENDOR_INTEL,      CARD_INTEL_UHD620,              "Intel(R) UHD Graphics 620",                                 DRIVER_INTEL_HD4000,  3072},
+    {HW_VENDOR_INTEL,      CARD_INTEL_UHD620_1,            "Intel(R) UHD Graphics 620",                                 DRIVER_INTEL_HD4000,  3072},
+    {HW_VENDOR_INTEL,      CARD_INTEL_UHD620_2,            "Intel(R) UHD Graphics 620",                                 DRIVER_INTEL_HD4000,  3072},
     {HW_VENDOR_INTEL,      CARD_INTEL_HD615,               "Intel(R) HD Graphics 615",                                  DRIVER_INTEL_HD4000,  2048},
     {HW_VENDOR_INTEL,      CARD_INTEL_HD620,               "Intel(R) HD Graphics 620",                                  DRIVER_INTEL_HD4000,  3072},
     {HW_VENDOR_INTEL,      CARD_INTEL_HD630_1,             "Intel(R) HD Graphics 630",                                  DRIVER_INTEL_HD4000,  3072},
diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c
index cf081a17c68e..772caa77c350 100644
--- a/dlls/wined3d/surface.c
+++ b/dlls/wined3d/surface.c
@@ -826,9 +826,6 @@ static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int
                     memcpy(dbuf, sbuf, row_byte_count);
                     dbuf += dst_map.row_pitch;
                 }
-
-                if (same_sub_resource)
-                    free(tmp_buffer);
             }
         }
         else
@@ -904,9 +901,6 @@ do { \
                 dbuf += dst_map.row_pitch;
                 last_sy = sy;
             }
-
-            if (same_sub_resource)
-                free(tmp_buffer);
         }
     }
     else
@@ -964,6 +958,19 @@ do { \
             dBottomLeft  = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
             dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
 
+            if (same_sub_resource &&
+                    !(dst_box->bottom <= src_box->top || src_box->bottom <= dst_box->top
+                    || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
+                    && fx->fx & (WINEDDBLTFX_MIRRORLEFTRIGHT | WINEDDBLTFX_MIRRORUPDOWN | WINEDDBLTFX_ROTATE180
+                    | WINEDDBLTFX_ROTATE270 | WINEDDBLTFX_ROTATE90))
+            {
+                if ((tmp_buffer = malloc(src_height * src_map.row_pitch)))
+                {
+                    memcpy(tmp_buffer, sbase, src_height * src_map.row_pitch);
+                    sbase = tmp_buffer;
+                }
+            }
+
             if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
             {
                 /* I don't think we need to do anything about this flag. */
@@ -1112,6 +1119,8 @@ error:
         FIXME("    Unsupported flags %#x.\n", flags);
 
 release:
+    free(tmp_buffer);
+
     if (upload && hr == WINED3D_OK)
     {
         struct wined3d_bo_address data;
diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c
index 4c20587b81db..2bc544c1213b 100644
--- a/dlls/wined3d/swapchain.c
+++ b/dlls/wined3d/swapchain.c
@@ -1549,12 +1549,6 @@ static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struc
 
     wined3d_mutex_lock();
 
-    if (desc->backbuffer_count > 1)
-    {
-        FIXME("The application requested more than one back buffer, this is not properly supported.\n"
-                "Please configure the application to use double buffering (1 back buffer) if possible.\n");
-    }
-
     if (desc->swap_effect != WINED3D_SWAP_EFFECT_DISCARD
             && desc->swap_effect != WINED3D_SWAP_EFFECT_SEQUENTIAL
             && desc->swap_effect != WINED3D_SWAP_EFFECT_COPY)
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index fa758c53cc16..313e441d7006 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -2409,7 +2409,8 @@ enum wined3d_pci_device
     CARD_INTEL_IPP580_1             = 0x193a,
     CARD_INTEL_IPP580_2             = 0x193d,
     CARD_INTEL_UHD617               = 0x87c0,
-    CARD_INTEL_UHD620               = 0x3ea0,
+    CARD_INTEL_UHD620_1             = 0x3ea0,
+    CARD_INTEL_UHD620_2             = 0x5917,
     CARD_INTEL_HD615                = 0x591e,
     CARD_INTEL_HD620                = 0x5916,
     CARD_INTEL_HD630_1              = 0x5912,
diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c
index 8cf203c9fb8f..38bb2a7ad69e 100644
--- a/dlls/winemac.drv/keyboard.c
+++ b/dlls/winemac.drv/keyboard.c
@@ -1179,6 +1179,8 @@ BOOL macdrv_ActivateKeyboardLayout(HKL hkl, UINT flags)
 
     TRACE("hkl %p flags %04x\n", hkl, flags);
 
+    NtUserPostMessage( NULL, WM_WINE_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, is_ime_hkl(hkl) );
+
     if (hkl == thread_data->active_keyboard_layout)
         return TRUE;
 
diff --git a/dlls/winemac.drv/mouse.c b/dlls/winemac.drv/mouse.c
index c655f56bd984..ee876d5355c1 100644
--- a/dlls/winemac.drv/mouse.c
+++ b/dlls/winemac.drv/mouse.c
@@ -665,9 +665,7 @@ BOOL macdrv_ClipCursor(const RECT *clip, BOOL reset)
 
     TRACE("%s %u\n", wine_dbgstr_rect(clip), reset);
 
-    if (reset) return TRUE;
-
-    if (clip)
+    if (!reset && clip)
     {
         rect = CGRectMake(clip->left, clip->top, max(1, clip->right - clip->left),
                           max(1, clip->bottom - clip->top));
diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c
index 2cc11b7dc1ea..7831eca732ba 100644
--- a/dlls/winemac.drv/window.c
+++ b/dlls/winemac.drv/window.c
@@ -859,7 +859,7 @@ static LRESULT move_window(HWND hwnd, WPARAM wparam)
     POINT capturePoint;
     LONG style = NtUserGetWindowLongW(hwnd, GWL_STYLE);
     BOOL moved = FALSE;
-    DWORD dwPoint = NtUserGetThreadInfo()->message_pos;
+    DWORD dwPoint = NtUserGetMessagePos();
     INT captionHeight;
     HMONITOR mon = 0;
     MONITORINFO info;
diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c
index 4136edf4fd8d..431264faf31a 100644
--- a/dlls/wineoss.drv/oss.c
+++ b/dlls/wineoss.drv/oss.c
@@ -51,6 +51,7 @@ struct oss_stream
     UINT flags;
     AUDCLNT_SHAREMODE share;
     HANDLE event;
+    HANDLE timer_thread;
 
     int fd;
 
@@ -222,13 +223,6 @@ static void get_default_device(EDataFlow flow, char device[OSS_DEVNODE_SIZE])
     return;
 }
 
-static NTSTATUS oss_main_loop(void *args)
-{
-    struct main_loop_params *params = args;
-    NtSetEvent(params->event, NULL);
-    return STATUS_SUCCESS;
-}
-
 static NTSTATUS oss_get_endpoint_ids(void *args)
 {
     struct get_endpoint_ids_params *params = args;
@@ -639,10 +633,10 @@ static NTSTATUS oss_release_stream(void *args)
     struct oss_stream *stream = handle_get_stream(params->stream);
     SIZE_T size;
 
-    if(params->timer_thread){
+    if(stream->timer_thread){
         stream->please_quit = TRUE;
-        NtWaitForSingleObject(params->timer_thread, FALSE, NULL);
-        NtClose(params->timer_thread);
+        NtWaitForSingleObject(stream->timer_thread, FALSE, NULL);
+        NtClose(stream->timer_thread);
     }
 
     close(stream->fd);
@@ -662,66 +656,6 @@ static NTSTATUS oss_release_stream(void *args)
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS oss_start(void *args)
-{
-    struct start_params *params = args;
-    struct oss_stream *stream = handle_get_stream(params->stream);
-
-    oss_lock(stream);
-
-    if((stream->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !stream->event)
-        return oss_unlock_result(stream, &params->result, AUDCLNT_E_EVENTHANDLE_NOT_SET);
-
-    if(stream->playing)
-        return oss_unlock_result(stream, &params->result, AUDCLNT_E_NOT_STOPPED);
-
-    stream->playing = TRUE;
-
-    return oss_unlock_result(stream, &params->result, S_OK);
-}
-
-static NTSTATUS oss_stop(void *args)
-{
-    struct stop_params *params = args;
-    struct oss_stream *stream = handle_get_stream(params->stream);
-
-    oss_lock(stream);
-
-    if(!stream->playing)
-        return oss_unlock_result(stream, &params->result, S_FALSE);
-
-    stream->playing = FALSE;
-    stream->in_oss_frames = 0;
-
-    return oss_unlock_result(stream, &params->result, S_OK);
-}
-
-static NTSTATUS oss_reset(void *args)
-{
-    struct reset_params *params = args;
-    struct oss_stream *stream = handle_get_stream(params->stream);
-
-    oss_lock(stream);
-
-    if(stream->playing)
-        return oss_unlock_result(stream, &params->result, AUDCLNT_E_NOT_STOPPED);
-
-    if(stream->getbuf_last)
-        return oss_unlock_result(stream, &params->result, AUDCLNT_E_BUFFER_OPERATION_PENDING);
-
-    if(stream->flow == eRender){
-        stream->written_frames = 0;
-        stream->last_pos_frames = 0;
-    }else{
-        stream->written_frames += stream->held_frames;
-    }
-    stream->held_frames = 0;
-    stream->lcl_offs_frames = 0;
-    stream->in_oss_frames = 0;
-
-    return oss_unlock_result(stream, &params->result, S_OK);
-}
-
 static void silence_buffer(struct oss_stream *stream, BYTE *buffer, UINT32 frames)
 {
     WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)stream->fmt;
@@ -869,10 +803,9 @@ static void oss_read_data(struct oss_stream *stream)
     }
 }
 
-static NTSTATUS oss_timer_loop(void *args)
+static void oss_timer_loop(void *args)
 {
-    struct timer_loop_params *params = args;
-    struct oss_stream *stream = handle_get_stream(params->stream);
+    struct oss_stream *stream = args;
     LARGE_INTEGER delay, now, next;
     int adjust;
 
@@ -907,8 +840,68 @@ static NTSTATUS oss_timer_loop(void *args)
     }
 
     oss_unlock(stream);
+}
 
-    return STATUS_SUCCESS;
+static NTSTATUS oss_start(void *args)
+{
+    struct start_params *params = args;
+    struct oss_stream *stream = handle_get_stream(params->stream);
+    static const WCHAR name[] = {'a','u','d','i','o','_','c','l','i','e','n','t','_','t','i','m','e','r',0};
+
+    oss_lock(stream);
+
+    if((stream->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !stream->event)
+        return oss_unlock_result(stream, &params->result, AUDCLNT_E_EVENTHANDLE_NOT_SET);
+
+    if(stream->playing)
+        return oss_unlock_result(stream, &params->result, AUDCLNT_E_NOT_STOPPED);
+
+    stream->playing = TRUE;
+    if (!stream->timer_thread) create_unix_thread( &stream->timer_thread, name, oss_timer_loop, stream );
+
+    return oss_unlock_result(stream, &params->result, S_OK);
+}
+
+static NTSTATUS oss_stop(void *args)
+{
+    struct stop_params *params = args;
+    struct oss_stream *stream = handle_get_stream(params->stream);
+
+    oss_lock(stream);
+
+    if(!stream->playing)
+        return oss_unlock_result(stream, &params->result, S_FALSE);
+
+    stream->playing = FALSE;
+    stream->in_oss_frames = 0;
+
+    return oss_unlock_result(stream, &params->result, S_OK);
+}
+
+static NTSTATUS oss_reset(void *args)
+{
+    struct reset_params *params = args;
+    struct oss_stream *stream = handle_get_stream(params->stream);
+
+    oss_lock(stream);
+
+    if(stream->playing)
+        return oss_unlock_result(stream, &params->result, AUDCLNT_E_NOT_STOPPED);
+
+    if(stream->getbuf_last)
+        return oss_unlock_result(stream, &params->result, AUDCLNT_E_BUFFER_OPERATION_PENDING);
+
+    if(stream->flow == eRender){
+        stream->written_frames = 0;
+        stream->last_pos_frames = 0;
+    }else{
+        stream->written_frames += stream->held_frames;
+    }
+    stream->held_frames = 0;
+    stream->lcl_offs_frames = 0;
+    stream->in_oss_frames = 0;
+
+    return oss_unlock_result(stream, &params->result, S_OK);
 }
 
 static NTSTATUS oss_get_render_buffer(void *args)
@@ -1641,14 +1634,14 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
 {
     oss_not_implemented,
     oss_not_implemented,
-    oss_main_loop,
+    oss_not_implemented,
+    oss_not_implemented,
     oss_get_endpoint_ids,
     oss_create_stream,
     oss_release_stream,
     oss_start,
     oss_stop,
     oss_reset,
-    oss_timer_loop,
     oss_get_render_buffer,
     oss_release_render_buffer,
     oss_get_capture_buffer,
@@ -1709,19 +1702,6 @@ static NTSTATUS oss_wow64_test_connect(void *args)
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS oss_wow64_main_loop(void *args)
-{
-    struct
-    {
-        PTR32 event;
-    } *params32 = args;
-    struct main_loop_params params =
-    {
-        .event = ULongToHandle(params32->event)
-    };
-    return oss_main_loop(&params);
-}
-
 static NTSTATUS oss_wow64_get_endpoint_ids(void *args)
 {
     struct
@@ -1786,13 +1766,11 @@ static NTSTATUS oss_wow64_release_stream(void *args)
     struct
     {
         stream_handle stream;
-        PTR32 timer_thread;
         HRESULT result;
     } *params32 = args;
     struct release_stream_params params =
     {
         .stream = params32->stream,
-        .timer_thread = ULongToHandle(params32->timer_thread)
     };
     oss_release_stream(&params);
     params32->result = params.result;
@@ -2146,14 +2124,14 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
 {
     oss_wow64_process_attach,
     oss_not_implemented,
-    oss_wow64_main_loop,
+    oss_not_implemented,
+    oss_not_implemented,
     oss_wow64_get_endpoint_ids,
     oss_wow64_create_stream,
     oss_wow64_release_stream,
     oss_start,
     oss_stop,
     oss_reset,
-    oss_timer_loop,
     oss_wow64_get_render_buffer,
     oss_release_render_buffer,
     oss_wow64_get_capture_buffer,
diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c
index fdae8ecc36a9..ef13a1ed0bbb 100644
--- a/dlls/winepulse.drv/pulse.c
+++ b/dlls/winepulse.drv/pulse.c
@@ -65,6 +65,7 @@ struct pulse_stream
     DWORD flags;
     AUDCLNT_SHAREMODE share;
     HANDLE event;
+    HANDLE timer_thread;
     float vol[PA_CHANNELS_MAX];
 
     REFERENCE_TIME def_period;
@@ -273,19 +274,47 @@ static void pulse_main_loop_thread_cleanup(void *context)
     pulse_broadcast();
 }
 
-static NTSTATUS pulse_main_loop(void *args)
+static void pulse_main_loop(void *args)
 {
-    struct main_loop_params *params = args;
+    HANDLE event = args;
     int ret;
     pulse_lock();
     pulse_ml = pa_mainloop_new();
     pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
-    NtSetEvent(params->event, NULL);
+    NtSetEvent(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();
+    PsTerminateSystemThread( 0 );
+}
+
+static HANDLE main_loop_thread;
+
+static NTSTATUS pulse_main_loop_start(void *args)
+{
+    static const WCHAR name[] = {'a','u','d','i','o','_','c','l','i','e','n','t','_','m','a','i','n',0};
+    HANDLE event;
+    NTSTATUS status;
+
+    if (main_loop_thread) return STATUS_SUCCESS;
+
+    NtCreateEvent( &event, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
+    if (!(status = create_unix_thread( &main_loop_thread, name, pulse_main_loop, event )))
+        NtWaitForSingleObject( event, FALSE, NULL );
+    NtClose( event );
+    return status;
+}
+
+static NTSTATUS pulse_main_loop_stop(void *args)
+{
+    if (main_loop_thread)
+    {
+        NtWaitForSingleObject( main_loop_thread, FALSE, NULL );
+        NtClose( main_loop_thread );
+        main_loop_thread = 0;
+    }
     return STATUS_SUCCESS;
 }
 
@@ -1233,10 +1262,10 @@ static NTSTATUS pulse_release_stream(void *args)
     struct pulse_stream *stream = handle_get_stream(params->stream);
     SIZE_T size;
 
-    if(params->timer_thread) {
+    if(stream->timer_thread) {
         stream->please_quit = TRUE;
-        NtWaitForSingleObject(params->timer_thread, FALSE, NULL);
-        NtClose(params->timer_thread);
+        NtWaitForSingleObject(stream->timer_thread, FALSE, NULL);
+        NtClose(stream->timer_thread);
     }
 
     pulse_lock();
@@ -1548,10 +1577,9 @@ static void pulse_read(struct pulse_stream *stream)
     }
 }
 
-static NTSTATUS pulse_timer_loop(void *args)
+static void pulse_timer_loop(void *args)
 {
-    struct timer_loop_params *params = args;
-    struct pulse_stream *stream = handle_get_stream(params->stream);
+    struct pulse_stream *stream = args;
     LARGE_INTEGER delay;
     pa_usec_t last_time;
     UINT32 adv_bytes;
@@ -1645,14 +1673,13 @@ static NTSTATUS pulse_timer_loop(void *args)
 
         pulse_unlock();
     }
-
-    return STATUS_SUCCESS;
 }
 
 static NTSTATUS pulse_start(void *args)
 {
     struct start_params *params = args;
     struct pulse_stream *stream = handle_get_stream(params->stream);
+    static const WCHAR name[] = {'a','u','d','i','o','_','c','l','i','e','n','t','_','t','i','m','e','r',0};
     int success;
 
     params->result = S_OK;
@@ -1694,6 +1721,7 @@ static NTSTATUS pulse_start(void *args)
         stream->just_started = TRUE;
     }
     pulse_unlock();
+    if (!stream->timer_thread) create_unix_thread( &stream->timer_thread, name, pulse_timer_loop, stream );
     return STATUS_SUCCESS;
 }
 
@@ -2504,14 +2532,14 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
 {
     pulse_process_attach,
     pulse_process_detach,
-    pulse_main_loop,
+    pulse_main_loop_start,
+    pulse_main_loop_stop,
     pulse_get_endpoint_ids,
     pulse_create_stream,
     pulse_release_stream,
     pulse_start,
     pulse_stop,
     pulse_reset,
-    pulse_timer_loop,
     pulse_get_render_buffer,
     pulse_release_render_buffer,
     pulse_get_capture_buffer,
@@ -2557,19 +2585,6 @@ static NTSTATUS pulse_wow64_process_attach(void *args)
     return pulse_process_attach( args );
 }
 
-static NTSTATUS pulse_wow64_main_loop(void *args)
-{
-    struct
-    {
-        PTR32 event;
-    } *params32 = args;
-    struct main_loop_params params =
-    {
-        .event = ULongToHandle(params32->event)
-    };
-    return pulse_main_loop(&params);
-}
-
 static NTSTATUS pulse_wow64_get_endpoint_ids(void *args)
 {
     struct
@@ -2634,13 +2649,11 @@ static NTSTATUS pulse_wow64_release_stream(void *args)
     struct
     {
         stream_handle stream;
-        PTR32 timer_thread;
         HRESULT result;
     } *params32 = args;
     struct release_stream_params params =
     {
         .stream = params32->stream,
-        .timer_thread = ULongToHandle(params32->timer_thread)
     };
     pulse_release_stream(&params);
     params32->result = params.result;
@@ -3011,14 +3024,14 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
 {
     pulse_wow64_process_attach,
     pulse_process_detach,
-    pulse_wow64_main_loop,
+    pulse_main_loop_start,
+    pulse_main_loop_stop,
     pulse_wow64_get_endpoint_ids,
     pulse_wow64_create_stream,
     pulse_wow64_release_stream,
     pulse_start,
     pulse_stop,
     pulse_reset,
-    pulse_timer_loop,
     pulse_wow64_get_render_buffer,
     pulse_release_render_buffer,
     pulse_wow64_get_capture_buffer,
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index 2983d7bfe8a6..e96b573083db 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -614,7 +614,7 @@ static void set_input_focus( struct x11drv_win_data *data )
     if (EVENT_x11_time_to_win32_time(0))
         /* ICCCM says don't use CurrentTime, so try to use last message time if possible */
         /* FIXME: this is not entirely correct */
-        timestamp = NtUserGetThreadInfo()->message_time - EVENT_x11_time_to_win32_time(0);
+        timestamp = NtUserGetThreadState(UserThreadStateMessageTime) - EVENT_x11_time_to_win32_time(0);
     else
         timestamp = CurrentTime;
 
@@ -1176,7 +1176,7 @@ static int get_window_wm_state( Display *display, Window window )
         XID     icon;
     } *state;
     Atom type;
-    int format, ret = -1;
+    int format, ret = WithdrawnState;
     unsigned long count, remaining;
 
     if (!XGetWindowProperty( display, window, x11drv_atom(WM_STATE), 0,
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c
index 65de84678ec1..4e6ccc11d65d 100644
--- a/dlls/winex11.drv/keyboard.c
+++ b/dlls/winex11.drv/keyboard.c
@@ -55,6 +55,7 @@
 #include "kbd.h"
 #include "wine/server.h"
 #include "wine/debug.h"
+#include "wine/list.h"
 
 /* log format (add 0-padding as appropriate):
     keycode  %u  as in output from xev
@@ -65,6 +66,18 @@
 WINE_DEFAULT_DEBUG_CHANNEL(keyboard);
 WINE_DECLARE_DEBUG_CHANNEL(key);
 
+struct layout
+{
+    struct list entry;
+
+    int xkb_group;
+    char *xkb_layout;
+
+    LANGID lang;
+    DWORD klid;
+    WORD layout_id;
+};
+
 static const unsigned int ControlMask = 1 << 2;
 
 static int min_keycode, max_keycode, keysyms_per_keycode;
@@ -73,9 +86,97 @@ static WORD keyc2vkey[256], keyc2scan[256];
 static int NumLockMask, ScrollLockMask, AltGrMask; /* mask in the XKeyEvent state */
 
 static pthread_mutex_t kbd_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct list xkb_layouts = LIST_INIT( xkb_layouts );
 
 static char KEYBOARD_MapDeadKeysym(KeySym keysym);
 
+static const WCHAR keyboard_layouts_keyW[] =
+{
+    '\\','R','e','g','i','s','t','r','y',
+    '\\','M','a','c','h','i','n','e',
+    '\\','S','y','s','t','e','m',
+    '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
+    '\\','C','o','n','t','r','o','l',
+    '\\','K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','s'
+};
+
+static ULONG query_reg_ascii_value( HKEY hkey, const char *name, KEY_VALUE_PARTIAL_INFORMATION *info, ULONG size )
+{
+    WCHAR nameW[64];
+
+    asciiz_to_unicode( nameW, name );
+    return query_reg_value( hkey, nameW, info, size );
+}
+
+static WORD get_layout_id_from_klid( DWORD klid )
+{
+    static WORD next_layout_id = 0x100;
+
+    char buffer[2048];
+    KEY_VALUE_PARTIAL_INFORMATION *value = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
+    WORD layout_id = 0;
+    HKEY hkey = NULL, subkey;
+    WCHAR name[16];
+
+    TRACE( "klid %08x\n", klid );
+
+    if (!HIWORD(klid)) return 0;
+    if (klid == -1) goto done;
+
+    snprintf( buffer, sizeof(buffer), "%08x", klid );
+    asciiz_to_unicode( name, buffer );
+
+    if (!(hkey = reg_open_key( NULL, keyboard_layouts_keyW, sizeof(keyboard_layouts_keyW) ))) goto done;
+
+    if (!(subkey = reg_open_key( hkey, name, 8 * sizeof(WCHAR) ))) goto done;
+
+    if (query_reg_ascii_value( subkey, "Layout Id", value, sizeof(buffer) ) && value->Type == REG_SZ)
+        layout_id = wcstoul( (const WCHAR *)value->Data, NULL, 16 );
+    NtClose( subkey );
+
+done:
+    if (hkey) NtClose( hkey );
+    if (!layout_id) layout_id = next_layout_id++;
+    TRACE( "layout_id %04x\n", layout_id );
+    return layout_id;
+}
+
+static void create_layout_from_xkb( int xkb_group, const char *xkb_layout, LANGID lang, DWORD klid )
+{
+    struct layout *layout;
+    WORD index = 0;
+
+    TRACE( "xkb_group %u, xkb_layout %s, lang %04x, klid %08x\n", xkb_group, xkb_layout, lang, klid );
+
+    LIST_FOR_EACH_ENTRY( layout, &xkb_layouts, struct layout, entry )
+    {
+        if (!strcmp( layout->xkb_layout, xkb_layout ))
+        {
+            TRACE( "Found existing layout entry %p, lang %04x, klid %08x, layout_id %04x\n",
+                   layout, layout->lang, layout->klid, layout->layout_id );
+            if (layout->xkb_group == -1) layout->xkb_group = xkb_group;
+            return;
+        }
+        if (lang == layout->lang && HIWORD(layout->klid) >= 0x20) index++;
+    }
+
+    if (!(layout = calloc( 1, sizeof(*layout) + strlen( xkb_layout ) + 1 )))
+    {
+        WARN( "Failed to allocate memory for Xkb layout entry\n" );
+        return;
+    }
+    list_add_tail( &xkb_layouts, &layout->entry );
+
+    layout->xkb_group = xkb_group;
+    layout->xkb_layout = strcpy( (char *)(layout + 1), xkb_layout );
+
+    layout->lang = lang;
+    layout->klid = !klid ? MAKELONG(lang, 0) : (klid == -1) ? MAKELONG(lang, index + 0x20) : klid;
+    layout->layout_id = get_layout_id_from_klid( klid );
+
+    TRACE( "Created layout entry %p, lang %04x, klid %08x, layout_id %04x\n", layout, layout->lang, layout->klid, layout->layout_id );
+}
+
 /* Keyboard translation tables */
 #define MAIN_LEN 49
 static const WORD main_key_scan_qwerty[MAIN_LEN] =
@@ -1588,7 +1689,7 @@ static int layout_id_map_cmp( const void *key, const void *element )
 
 static LANGID langid_from_xkb_layout( const char *layout )
 {
-    struct layout_id_map_entry *entry;
+    const struct layout_id_map_entry *entry;
 
     entry = bsearch( layout, layout_ids, ARRAY_SIZE(layout_ids), sizeof(*layout_ids), layout_id_map_cmp );
     if (entry) return entry->langid;
@@ -1597,6 +1698,43 @@ static LANGID langid_from_xkb_layout( const char *layout )
     return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED);
 };
 
+static const struct klid_map_entry
+{
+    const char *layout;
+    const char *variant;
+    DWORD klid;
+} klid_map[] =
+{
+    { "us", "dvorak", 0x00010409 },
+    { "us", "dvorak-l", 0x00030409 },
+    { "us", "dvorak-r", 0x00040409 },
+};
+
+static int klid_map_cmp( const void *key, const void *element )
+{
+    const struct klid_map_entry *map_key = key;
+    const struct klid_map_entry *entry = element;
+    int c;
+
+    if ((c = strcmp( map_key->layout, entry->layout ))) return c;
+    return strcmp( map_key->variant, entry->variant );
+}
+
+static DWORD klid_from_xkb_layout( const char *layout, const char *variant )
+{
+    struct klid_map_entry key = { layout, variant };
+    const struct klid_map_entry *entry;
+
+    if (!variant)
+        return 0;
+
+    entry = bsearch( &key, klid_map, ARRAY_SIZE(klid_map), sizeof(*klid_map), klid_map_cmp );
+    if (entry) return entry->klid;
+
+    FIXME( "Unknown variant %s\n", debugstr_a(variant) );
+    return -1;
+}
+
 /* fuzzy layout detection through keysym / keycode matching, kbd_section must be held */
 static void detect_keyboard_layout( Display *display, XModifierKeymap *modmap, unsigned int xkb_group )
 {
@@ -1960,6 +2098,7 @@ void init_keyboard_layouts( Display *display )
     XkbStateRec xkb_state;
     XModifierKeymap *mmp;
     XkbDescRec *xkb_desc;
+    struct layout *entry;
     LANGID xkb_lang = 0;
     Status status;
     KeyCode *kcp;
@@ -1990,6 +2129,10 @@ void init_keyboard_layouts( Display *display )
         }
     }
 
+    /* Flag any previously created Xkb layout as invalid */
+    LIST_FOR_EACH_ENTRY( entry, &xkb_layouts, struct layout, entry )
+        entry->xkb_group = -1;
+
     status = XkbGetState( display, XkbUseCoreKbd, &xkb_state );
     xkb_group = status ? 0 : xkb_state.group;
     TRACE( "current group %u (status %#x)\n", xkb_group, status );
@@ -2004,19 +2147,26 @@ void init_keyboard_layouts( Display *display )
             if (!xkb_desc->names->groups[count]) break;
 
         if (!XGetAtomNames( display, xkb_desc->names->groups, count, names )) count = 0;
+        TRACE("Found %u group names\n", count);
         for (int i = 0; i < count; i++)
         {
             const char *layout, *variant = NULL;
+            char buffer[1024];
+            DWORD klid;
             LANGID lang;
 
             if (!names[i]) continue;
             if (find_xkb_layout_variant( names[i], &layout, &variant ))
             {
                 lang = langid_from_xkb_layout( layout );
+                klid = klid_from_xkb_layout( layout, variant );
                 if (i == xkb_group) xkb_lang = lang;
 
-                TRACE( "Found group %u with name %s -> layout %s:%s, lang %04x\n", i, debugstr_a(names[i]),
-                       debugstr_a(layout), debugstr_a(variant), lang );
+                TRACE( "Found group %u with name %s -> layout %s:%s, lang %04x, klid %08x\n", i, debugstr_a(names[i]),
+                       debugstr_a(layout), debugstr_a(variant), lang, klid );
+
+                snprintf( buffer, ARRAY_SIZE(buffer), "%s:%s", layout, variant );
+                create_layout_from_xkb( i, buffer, lang, klid );
             }
             XFree( names[i] );
         }
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c
index d2d53a40be39..c3d3312581d7 100644
--- a/dlls/winex11.drv/window.c
+++ b/dlls/winex11.drv/window.c
@@ -1588,6 +1588,10 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, B
      * to WithdrawnState first, then to NormalState */
     if (data->managed && MAKELONG(old_state, new_state) == MAKELONG(IconicState, NormalState))
     {
+        /* Previous IconicState request is still pending, wait for it to complete so that there is no
+         * unexpected IconicState WM_STATE notify that will override the NormalState soon to be queued */
+        if (data->wm_state_serial) return;
+
         WARN( "window %p/%lx is iconic, remapping to workaround Mutter issues.\n", data->hwnd, data->whole_window );
         window_set_wm_state( data, WithdrawnState, FALSE );
         window_set_wm_state( data, NormalState, activate );
diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c
index a53f83bdd68c..ae746db955fb 100644
--- a/dlls/wininet/http.c
+++ b/dlls/wininet/http.c
@@ -1158,6 +1158,7 @@ static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
                                                 in.pvBuffer ? &in_desc : NULL,
                                                 0, &pAuthInfo->ctx, &out_desc,
                                                 &pAuthInfo->attr, &pAuthInfo->exp);
+        free(in.pvBuffer);
         if (sec_status == SEC_E_OK)
         {
             pAuthInfo->finished = TRUE;
diff --git a/dlls/winsta/Makefile.in b/dlls/winsta/Makefile.in
index 7403a3183cfc..9b975b96a212 100644
--- a/dlls/winsta/Makefile.in
+++ b/dlls/winsta/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = winsta.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	main.c
diff --git a/dlls/wkscli/Makefile.in b/dlls/wkscli/Makefile.in
new file mode 100644
index 000000000000..40516b96f8c6
--- /dev/null
+++ b/dlls/wkscli/Makefile.in
@@ -0,0 +1,6 @@
+MODULE    = wkscli.dll
+IMPORTLIB = wkscli
+IMPORTS   = netutils
+
+SOURCES = \
+	wkscli.c
diff --git a/dlls/wkscli/wkscli.c b/dlls/wkscli/wkscli.c
new file mode 100644
index 000000000000..016a751a9678
--- /dev/null
+++ b/dlls/wkscli/wkscli.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2026 Alistair Leslie-Hughes
+ *
+ * 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 <stdarg.h>
+
+#include "ntstatus.h"
+#include "windef.h"
+#include "winbase.h"
+
+#include "lm.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wkscli);
+
+/************************************************************
+ *                NetGetJoinInformation (wkscli.@)
+ */
+NET_API_STATUS NET_API_FUNCTION NetGetJoinInformation(
+    LPCWSTR Server,
+    LPWSTR *Name,
+    PNETSETUP_JOIN_STATUS type)
+{
+    static const WCHAR workgroupW[] = L"Workgroup";
+
+    FIXME("Semi-stub %s %p %p\n", wine_dbgstr_w(Server), Name, type);
+
+    if (!Name || !type)
+        return ERROR_INVALID_PARAMETER;
+
+    NetApiBufferAllocate(sizeof(workgroupW), (LPVOID *)Name);
+    lstrcpyW(*Name, workgroupW);
+    *type = NetSetupWorkgroupName;
+
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NetUseAdd (wkscli.@)
+ */
+NET_API_STATUS WINAPI NetUseAdd(LMSTR servername, DWORD level, LPBYTE bufptr, LPDWORD parm_err)
+{
+    FIXME("%s %ld %p %p stub\n", debugstr_w(servername), level, bufptr, parm_err);
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NetUseDel (wkscli.@)
+ */
+NET_API_STATUS WINAPI NetUseDel(LMSTR servername, LMSTR usename, DWORD forcecond)
+{
+    FIXME("%s %s %ld stub\n", debugstr_w(servername), debugstr_w(usename), forcecond);
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NetUseEnum (wkscli.@)
+ */
+NET_API_STATUS WINAPI NetUseEnum(LMSTR server, DWORD level, LPBYTE* bufptr, DWORD prefmaxsize,
+                                  LPDWORD entriesread, LPDWORD totalentries, LPDWORD resumehandle)
+{
+    FIXME("stub (%p, %ld, %p, %ld, %p, %p, %p)\n", server, level, bufptr, prefmaxsize,
+           entriesread, totalentries, resumehandle);
+    return ERROR_NOT_SUPPORTED;
+}
+
+/************************************************************
+ *                NetUseGetInfo (wkscli.@)
+ */
+NET_API_STATUS WINAPI NetUseGetInfo(LMSTR server, LMSTR name, DWORD level, LPBYTE *bufptr)
+{
+    FIXME("stub (%p, %p, %ld, %p)\n", server, name, level, bufptr);
+    return ERROR_NOT_SUPPORTED;
+}
+
+/************************************************************
+ *                NetWkstaTransportEnum (wkscli.@)
+ */
+NET_API_STATUS WINAPI NetWkstaTransportEnum(LMSTR ServerName, DWORD level, PBYTE* pbuf,
+      DWORD prefmaxlen, LPDWORD read_entries,
+      PDWORD total_entries, PDWORD hresume)
+{
+    FIXME(":%s, 0x%08lx, %p, 0x%08lx, %p, %p, %p\n", debugstr_w(ServerName),
+            level, pbuf, prefmaxlen, read_entries, total_entries,hresume);
+    return ERROR_NOT_SUPPORTED;
+}
diff --git a/dlls/wkscli/wkscli.spec b/dlls/wkscli/wkscli.spec
new file mode 100755
index 000000000000..1c3b33577f31
--- /dev/null
+++ b/dlls/wkscli/wkscli.spec
@@ -0,0 +1,23 @@
+@ stub NetAddAlternateComputerName
+@ stub NetEnumerateComputerNames
+@ stdcall NetGetJoinInformation( wstr ptr ptr )
+@ stub NetGetJoinableOUs
+@ stub NetJoinDomain
+@ stub NetRemoveAlternateComputerName
+@ stub NetRenameMachineInDomain
+@ stub NetSetPrimaryComputerName
+@ stub NetUnjoinDomain
+@ stdcall NetUseAdd( str long ptr ptr )
+@ stdcall NetUseDel( str str long )
+@ stdcall NetUseEnum( str long ptr long ptr ptr ptr )
+@ stdcall NetUseGetInfo( str str long ptr )
+@ stub NetValidateName
+@ stub NetWkstaGetInfo( str long ptr )
+@ stub NetWkstaSetInfo
+@ stub NetWkstaStatisticsGet
+@ stub NetWkstaTransportAdd
+@ stub NetWkstaTransportDel
+@ stdcall NetWkstaTransportEnum( str long ptr long ptr ptr ptr )
+@ stub NetWkstaUserEnum( str long ptr long ptr ptr ptr )
+@ stub NetWkstaUserGetInfo( str long ptr )
+@ stub NetWkstaUserSetInfo
diff --git a/dlls/wmp/Makefile.in b/dlls/wmp/Makefile.in
index b566ff3bb4ff..7218f7a0c555 100644
--- a/dlls/wmp/Makefile.in
+++ b/dlls/wmp/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = wmp.dll
 IMPORTS   = oleaut32 ole32 urlmon shlwapi user32 gdi32
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine Media Player"
 VER_PRODUCTVERSION      = 12,0,7601,16982
 
diff --git a/dlls/wmvcore/Makefile.in b/dlls/wmvcore/Makefile.in
index 7f3a8553f621..bf9d47c986bf 100644
--- a/dlls/wmvcore/Makefile.in
+++ b/dlls/wmvcore/Makefile.in
@@ -3,8 +3,6 @@ IMPORTLIB = wmvcore
 IMPORTS   = kernel32 combase
 DELAYIMPORTS = winegstreamer
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 VER_FILEDESCRIPTION_STR = "Wine Media Playback dll"
 VER_PRODUCTVERSION      = 12,0,7601,17514
 
diff --git a/dlls/wow64/struct32.h b/dlls/wow64/struct32.h
index de378c13cc24..ba2bc02523f2 100644
--- a/dlls/wow64/struct32.h
+++ b/dlls/wow64/struct32.h
@@ -749,4 +749,113 @@ typedef struct
     LARGE_INTEGER TimeLimit;
 } QUOTA_LIMITS32;
 
+typedef struct
+{
+    DWORD Length;
+    SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
+    SECURITY_CONTEXT_TRACKING_MODE ContextTrackingMode;
+    BOOLEAN EffectiveOnly;
+} SECURITY_QUALITY_OF_SERVICE32;
+
+typedef struct
+{
+    ULONG Flags;
+    SECURITY_QUALITY_OF_SERVICE32 SecurityQos;
+    ULONG MaxMessageLength;
+    ULONG MemoryBandwidth;
+    ULONG MaxPoolUsage;
+    ULONG MaxSectionSize;
+    ULONG MaxViewSize;
+    ULONG MaxTotalSectionSize;
+    ULONG DupObjectTypes;
+} ALPC_PORT_ATTRIBUTES32;
+
+typedef struct
+{
+    union
+    {
+        struct
+        {
+            USHORT DataLength;
+            USHORT TotalLength;
+        } DUMMYSTRUCTNAME1;
+        ULONG Length;
+    } DUMMYUNIONNAME1;
+    union
+    {
+        struct
+        {
+            USHORT Type;
+            USHORT DataInfoOffset;
+        } DUMMYSTRUCTNAME2;
+        ULONG ZeroInit;
+    } DUMMYUNIONNAME2;
+    union
+    {
+        CLIENT_ID32 ClientId;
+        double DoNotUseThisField;
+    } DUMMYUNIONNAME3;
+    ULONG MessageId;
+    union
+    {
+        ULONG ClientViewSize;
+        ULONG CallbackId;
+    } DUMMYUNIONNAME4;
+} ALPC_PORT_MESSAGE32, *PALPC_PORT_MESSAGE32, ALPC_PORT_MESSAGE_HEADER32, *PALPC_PORT_MESSAGE_HEADER32;
+
+typedef struct
+{
+    ULONG AllocatedAttributes;
+    ULONG ValidAttributes;
+} ALPC_MESSAGE_ATTRIBUTES32, *PALPC_MESSAGE_ATTRIBUTES32;
+
+typedef struct
+{
+    ULONG Flags;
+    ULONG QoSPointer;
+    ULONG ContextHandle;
+} ALPC_SECURITY_ATTR32, *PALPC_SECURITY_ATTR32;
+
+typedef struct
+{
+    ULONG Flags;
+    ULONG SectionHandle;
+    ULONG ViewBase;
+    ULONG ViewSize;
+} ALPC_VIEW_ATTR32, *PALPC_VIEW_ATTR32;
+
+typedef struct
+{
+    ULONG PortContext;
+    ULONG MessageContext;
+    ULONG Sequence;
+    ULONG MessageId;
+    ULONG CallbackId;
+} ALPC_CONTEXT_ATTR32, *PALPC_CONTEXT_ATTR32;
+
+typedef struct
+{
+    ULONG Flags;
+    ULONG Handle;
+    ULONG ObjectType;
+    ULONG DesiredAccess;
+} ALPC_HANDLE_ATTR32, *PALPC_HANDLE_ATTR32;
+
+typedef struct
+{
+    ULONGLONG TokenId;
+    ULONGLONG AuthenticationId;
+    ULONGLONG ModifiedId;
+} ALPC_TOKEN_ATTR32, *PALPC_TOKEN_ATTR32;
+
+typedef struct
+{
+    ULONG Event;
+} ALPC_DIRECT_ATTR32, *PALPC_DIRECT_ATTR32;
+
+typedef struct
+{
+    ULONGLONG Ticket;
+} ALPC_WORK_ON_BEHALF_ATTR32, *PALPC_WORK_ON_BEHALF_ATTR32;
+
 #endif /* __WOW64_STRUCT32_H */
diff --git a/dlls/wow64/syscall.c b/dlls/wow64/syscall.c
index 9fdef4a6d9e8..d52c52fef8e9 100644
--- a/dlls/wow64/syscall.c
+++ b/dlls/wow64/syscall.c
@@ -463,6 +463,173 @@ NTSTATUS WINAPI wow64_NtAllocateUuids( UINT *args )
     return NtAllocateUuids( time, delta, sequence, seed );
 }
 
+/**********************************************************************
+ *           wow64_NtAlpcAcceptConnectPort
+ */
+NTSTATUS WINAPI wow64_NtAlpcAcceptConnectPort( UINT *args )
+{
+    ULONG *communication_port_ptr = get_ptr( &args );
+    HANDLE connection_port = get_handle( &args );
+    ULONG flags = get_ulong( &args );
+    OBJECT_ATTRIBUTES32 *attr32 = get_ptr( &args );
+    ALPC_PORT_ATTRIBUTES32 *port_attr32 = get_ptr( &args );
+    void *context = get_ptr( &args );
+    ALPC_PORT_MESSAGE32 *msg32 = get_ptr( &args );
+    ALPC_MESSAGE_ATTRIBUTES32 *msg_attr32 = get_ptr( &args );
+    BOOLEAN accept = get_ulong( &args );
+    NTSTATUS status;
+
+    HANDLE communication_port = 0;
+    struct object_attr64 attr;
+    ALPC_PORT_ATTRIBUTES port_attr;
+    ALPC_PORT_MESSAGE *msg;
+    ALPC_MESSAGE_ATTRIBUTES *msg_attr;
+
+    status = NtAlpcAcceptConnectPort( communication_port_ptr ? &communication_port : NULL,
+                                      connection_port, flags, objattr_32to64( &attr, attr32 ),
+                                      alpc_port_attributes_32to64( &port_attr, port_attr32 ), context,
+                                      alpc_port_message_32to64( &msg, msg32 ? (sizeof(*msg) + msg32->DataLength) : 0, msg32, TRUE ),
+                                      alpc_port_message_attributes_32to64( &msg_attr, msg_attr32, TRUE ),
+                                      accept);
+    if (status == STATUS_SUCCESS && accept && communication_port_ptr)
+        put_handle( communication_port_ptr, communication_port );
+    return status;
+}
+
+/**********************************************************************
+ *           wow64_NtAlpcConnectPort
+ */
+NTSTATUS WINAPI wow64_NtAlpcConnectPort( UINT *args )
+{
+    ULONG *handle_ptr = get_ptr( &args );
+    UNICODE_STRING32 *str32 = get_ptr( &args );
+    OBJECT_ATTRIBUTES32 *attr32 = get_ptr( &args );
+    ALPC_PORT_ATTRIBUTES32 *port_attr32 = get_ptr( &args );
+    ULONG flags = get_ulong( &args );
+    SID *sid = get_ptr( &args );
+    ALPC_PORT_MESSAGE32 *msg32 = get_ptr( &args );
+    ULONG *size32 = get_ptr( &args );
+    ALPC_MESSAGE_ATTRIBUTES32 *send_msg_attr32 = get_ptr( &args );
+    ALPC_MESSAGE_ATTRIBUTES32 *recv_msg_attr32 = get_ptr( &args );
+    LARGE_INTEGER *timeout = get_ptr( &args );
+    NTSTATUS status;
+
+    HANDLE handle = 0;
+    UNICODE_STRING str;
+    struct object_attr64 attr;
+    ALPC_PORT_ATTRIBUTES port_attr;
+    ALPC_PORT_MESSAGE *msg;
+    SIZE_T size = size32 ? (*size32 + sizeof(ALPC_PORT_MESSAGE) - sizeof(ALPC_PORT_MESSAGE32)) : 65535;
+    ALPC_MESSAGE_ATTRIBUTES *send_msg_attr;
+    ALPC_MESSAGE_ATTRIBUTES *recv_msg_attr;
+
+    if (!handle_ptr) return STATUS_OBJECT_NAME_NOT_FOUND;
+
+    if (port_attr32 && (!port_attr32->MaxMessageLength
+                        || port_attr32->MaxMessageLength > (65535 - (sizeof(ALPC_PORT_MESSAGE) - sizeof(ALPC_PORT_MESSAGE32)))))
+        return STATUS_INVALID_PARAMETER;
+
+    status = NtAlpcConnectPort( &handle, unicode_str_32to64( &str, str32 ), objattr_32to64( &attr, attr32 ),
+                                alpc_port_attributes_32to64( &port_attr, port_attr32 ), flags, sid,
+                                alpc_port_message_32to64( &msg, size, msg32, TRUE ), &size,
+                                alpc_port_message_attributes_32to64( &send_msg_attr, send_msg_attr32, TRUE ),
+                                alpc_port_message_attributes_32to64( &recv_msg_attr, recv_msg_attr32, FALSE ), timeout);
+    if (status == STATUS_SUCCESS)
+    {
+        put_handle( handle_ptr, handle );
+        alpc_port_message_64to32( msg32, msg );
+        alpc_port_message_attributes_64to32( recv_msg_attr32, recv_msg_attr );
+    }
+    else if (status == STATUS_BUFFER_TOO_SMALL && size32)
+    {
+        put_size( size32, size - (sizeof(ALPC_PORT_MESSAGE) - sizeof(ALPC_PORT_MESSAGE32)));
+    }
+    return status;
+}
+
+/**********************************************************************
+ *           wow64_NtAlpcCreatePort
+ */
+NTSTATUS WINAPI wow64_NtAlpcCreatePort( UINT *args )
+{
+    ULONG *handle_ptr = get_ptr( &args );
+    OBJECT_ATTRIBUTES32 *attr32 = get_ptr( &args );
+    ALPC_PORT_ATTRIBUTES32 *port_attr32 = get_ptr( &args );
+    NTSTATUS status;
+
+    struct object_attr64 attr;
+    ALPC_PORT_ATTRIBUTES port_attr;
+    HANDLE handle = 0;
+
+    if (!handle_ptr) return STATUS_ACCESS_VIOLATION;
+
+    status = NtAlpcCreatePort( &handle, objattr_32to64( &attr, attr32 ),
+                               alpc_port_attributes_32to64( &port_attr, port_attr32 ) );
+    if (!status) put_handle( handle_ptr, handle );
+    return status;
+}
+
+/**********************************************************************
+ *           wow64_NtAlpcDisconnectPort
+ */
+NTSTATUS WINAPI wow64_NtAlpcDisconnectPort( UINT *args )
+{
+    HANDLE handle = get_handle( &args );
+    ULONG flags = get_ulong( &args );
+
+    return NtAlpcDisconnectPort( handle, flags );
+}
+
+/**********************************************************************
+ *           wow64_NtAlpcImpersonateClientOfPort
+ */
+NTSTATUS WINAPI wow64_NtAlpcImpersonateClientOfPort( UINT *args )
+{
+    HANDLE handle = get_handle( &args );
+    ALPC_PORT_MESSAGE32 *msg32 = get_ptr( &args );
+    void *reserved = get_ptr( &args );
+
+    ALPC_PORT_MESSAGE *msg;
+
+    return NtAlpcImpersonateClientOfPort( handle, alpc_port_message_32to64( &msg, msg32 ? (sizeof(*msg) + msg32->DataLength) : 0, msg32, TRUE ), reserved );
+}
+
+/**********************************************************************
+ *           wow64_NtAlpcSendWaitReceivePort
+ */
+NTSTATUS WINAPI wow64_NtAlpcSendWaitReceivePort( UINT *args )
+{
+    HANDLE handle = get_handle( &args );
+    ULONG flags = get_ulong( &args );
+    ALPC_PORT_MESSAGE32 *send_msg32 = get_ptr( &args );
+    ALPC_MESSAGE_ATTRIBUTES32 *send_msg_attr32 = get_ptr( &args );
+    ALPC_PORT_MESSAGE32 *recv_msg32 = get_ptr( &args );
+    ULONG *size32 = get_ptr( &args );
+    ALPC_MESSAGE_ATTRIBUTES32 *recv_msg_attr32 = get_ptr( &args );
+    LARGE_INTEGER *timeout = get_ptr( &args );
+    NTSTATUS status;
+
+    ALPC_PORT_MESSAGE *send_msg;
+    ALPC_MESSAGE_ATTRIBUTES *send_msg_attr;
+    ALPC_PORT_MESSAGE *recv_msg;
+    ALPC_MESSAGE_ATTRIBUTES *recv_msg_attr;
+    SIZE_T size = size32 ? (*size32 + sizeof(ALPC_PORT_MESSAGE) - sizeof(ALPC_PORT_MESSAGE32)) : 65535;
+
+    status = NtAlpcSendWaitReceivePort( handle, flags, alpc_port_message_32to64( &send_msg, send_msg32 ? (sizeof(*send_msg) + send_msg32->DataLength) : 0, send_msg32, TRUE ),
+                                        alpc_port_message_attributes_32to64( &send_msg_attr, send_msg_attr32, TRUE ),
+                                        alpc_port_message_32to64( &recv_msg, size, recv_msg32, FALSE ), &size,
+                                        alpc_port_message_attributes_32to64( &recv_msg_attr, recv_msg_attr32, FALSE ), timeout );
+    if (status == STATUS_SUCCESS)
+    {
+        alpc_port_message_64to32( recv_msg32, recv_msg );
+        alpc_port_message_attributes_64to32( recv_msg_attr32, recv_msg_attr );
+    }
+    else if (status == STATUS_BUFFER_TOO_SMALL && size32)
+    {
+        put_size( size32, size - (sizeof(ALPC_PORT_MESSAGE) - sizeof(ALPC_PORT_MESSAGE32)) );
+    }
+    return status;
+}
 
 /***********************************************************************
  *           wow64_NtCallbackReturn
diff --git a/dlls/wow64/wow64_private.h b/dlls/wow64/wow64_private.h
index deca72d445c5..2ac3e51f1f0a 100644
--- a/dlls/wow64/wow64_private.h
+++ b/dlls/wow64/wow64_private.h
@@ -194,6 +194,299 @@ static inline OBJECT_ATTRIBUTES *objattr_32to64_redirect( struct object_attr64 *
     return attr;
 }
 
+static inline ALPC_PORT_ATTRIBUTES *alpc_port_attributes_32to64( ALPC_PORT_ATTRIBUTES *out,
+                                                                 const ALPC_PORT_ATTRIBUTES32 *in )
+{
+    if (!in) return NULL;
+
+    out->Flags = in->Flags;
+    out->SecurityQos.Length = in->SecurityQos.Length;
+    out->SecurityQos.ImpersonationLevel = in->SecurityQos.ImpersonationLevel;
+    out->SecurityQos.ContextTrackingMode = in->SecurityQos.ContextTrackingMode;
+    out->SecurityQos.EffectiveOnly = in->SecurityQos.EffectiveOnly;
+    out->MaxMessageLength = in->MaxMessageLength + (sizeof(ALPC_PORT_MESSAGE) - sizeof(ALPC_PORT_MESSAGE32));
+    out->MemoryBandwidth = in->MemoryBandwidth;
+    out->MaxPoolUsage = in->MaxPoolUsage;
+    out->MaxSectionSize = in->MaxSectionSize;
+    out->MaxViewSize = in->MaxViewSize;
+    out->MaxTotalSectionSize = in->MaxTotalSectionSize;
+    out->DupObjectTypes = in->DupObjectTypes;
+    out->Reserved = 0;
+    return out;
+}
+
+static inline ALPC_PORT_MESSAGE *alpc_port_message_32to64( ALPC_PORT_MESSAGE **out, SIZE_T out_msg_size,
+                                                           const ALPC_PORT_MESSAGE32 *in, BOOL copy_msg )
+{
+    ALPC_PORT_MESSAGE *msg;
+
+    if (!in || !(msg = Wow64AllocateTemp( out_msg_size )))
+    {
+        *out = NULL;
+        return NULL;
+    }
+
+    if (!copy_msg) goto done;
+
+    msg->DataLength = in->DataLength;
+    msg->TotalLength = sizeof(*msg) + msg->DataLength;
+    msg->Type = in->Type;
+    msg->DataInfoOffset = in->DataInfoOffset;
+    client_id_32to64( &msg->ClientId, &in->ClientId );
+    msg->MessageId = in->MessageId;
+    msg->ClientViewSize = in->ClientViewSize;
+    memcpy( (unsigned char *)msg + sizeof(*msg), (const unsigned char *)in + sizeof(*in), in->DataLength );
+done:
+    *out = msg;
+    return msg;
+}
+
+static inline ALPC_MESSAGE_ATTRIBUTES *alpc_port_message_attributes_32to64( ALPC_MESSAGE_ATTRIBUTES **out,
+                                                                            const ALPC_MESSAGE_ATTRIBUTES32 *in,
+                                                                            BOOL copy_attributes )
+{
+    ALPC_MESSAGE_ATTRIBUTES *attr;
+    const unsigned char *current_from_attr;
+
+    if (!in || !(attr = Wow64AllocateTemp( AlpcGetHeaderSize( in->AllocatedAttributes ) )))
+    {
+        *out = NULL;
+        return NULL;
+    }
+
+    attr->AllocatedAttributes = in->AllocatedAttributes;
+
+    if (!copy_attributes)
+    {
+        attr->ValidAttributes = 0;
+        *out = attr;
+        return attr;
+    }
+
+    attr->ValidAttributes = in->ValidAttributes;
+    current_from_attr = (const unsigned char *)in + sizeof(*in);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_SECURITY_ATTRIBUTE)
+    {
+        const ALPC_SECURITY_ATTR32 *from_attr = (const ALPC_SECURITY_ATTR32 *)current_from_attr;
+        ALPC_SECURITY_ATTR *to_attr = AlpcGetMessageAttribute( attr, ALPC_MESSAGE_SECURITY_ATTRIBUTE );
+
+        to_attr->Flags = from_attr->Flags;
+        to_attr->ContextHandle = UlongToHandle( from_attr->ContextHandle );
+        if (from_attr->QoSPointer)
+        {
+            SECURITY_QUALITY_OF_SERVICE32 *qos32 = (SECURITY_QUALITY_OF_SERVICE32 *)ULongToPtr( from_attr->QoSPointer );
+            SECURITY_QUALITY_OF_SERVICE *qos = Wow64AllocateTemp( sizeof(*qos) );
+
+            to_attr->QoS = qos;
+            qos->Length = qos32->Length;
+            qos->ImpersonationLevel = qos32->ImpersonationLevel;
+            qos->ContextTrackingMode = qos32->ContextTrackingMode;
+            qos->EffectiveOnly = qos32->EffectiveOnly;
+        }
+        else
+        {
+            to_attr->QoS = NULL;
+        }
+    }
+    if (in->AllocatedAttributes & ALPC_MESSAGE_SECURITY_ATTRIBUTE)
+        current_from_attr += sizeof(ALPC_SECURITY_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_VIEW_ATTRIBUTE)
+    {
+        const ALPC_VIEW_ATTR32 *from_attr = (const ALPC_VIEW_ATTR32 *)current_from_attr;
+        ALPC_VIEW_ATTR *to_attr = AlpcGetMessageAttribute( attr, ALPC_MESSAGE_VIEW_ATTRIBUTE );
+
+        to_attr->Flags = from_attr->Flags;
+        to_attr->SectionHandle = UlongToHandle( from_attr->SectionHandle );
+        to_attr->ViewBase = UlongToPtr( from_attr->ViewBase );
+        to_attr->ViewSize = from_attr->ViewSize;
+    }
+    if (in->AllocatedAttributes & ALPC_MESSAGE_VIEW_ATTRIBUTE)
+        current_from_attr += sizeof(ALPC_VIEW_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_CONTEXT_ATTRIBUTE)
+    {
+        const ALPC_CONTEXT_ATTR32 *from_attr = (const ALPC_CONTEXT_ATTR32 *)current_from_attr;
+        ALPC_CONTEXT_ATTR *to_attr = AlpcGetMessageAttribute( attr, ALPC_MESSAGE_CONTEXT_ATTRIBUTE );
+
+        to_attr->PortContext = UlongToPtr( from_attr->PortContext );
+        to_attr->MessageContext = UlongToPtr( from_attr->MessageContext );
+        to_attr->Sequence = from_attr->Sequence;
+        to_attr->MessageId = from_attr->MessageId;
+        to_attr->CallbackId = from_attr->CallbackId;
+    }
+    if (in->AllocatedAttributes & ALPC_MESSAGE_CONTEXT_ATTRIBUTE)
+        current_from_attr += sizeof(ALPC_CONTEXT_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_HANDLE_ATTRIBUTE)
+    {
+        const ALPC_HANDLE_ATTR32 *from_attr = (const ALPC_HANDLE_ATTR32 *)current_from_attr;
+        ALPC_HANDLE_ATTR *to_attr = AlpcGetMessageAttribute( attr, ALPC_MESSAGE_HANDLE_ATTRIBUTE );
+
+        to_attr->Flags = from_attr->Flags;
+        to_attr->Handle = UlongToHandle( from_attr->Handle );
+        to_attr->ObjectType = from_attr->ObjectType;
+        to_attr->DesiredAccess = from_attr->DesiredAccess;
+    }
+    if (in->AllocatedAttributes & ALPC_MESSAGE_HANDLE_ATTRIBUTE)
+        current_from_attr += sizeof(ALPC_HANDLE_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_TOKEN_ATTRIBUTE)
+    {
+        const ALPC_TOKEN_ATTR32 *from_attr = (const ALPC_TOKEN_ATTR32 *)current_from_attr;
+        ALPC_TOKEN_ATTR *to_attr = AlpcGetMessageAttribute( attr, ALPC_MESSAGE_TOKEN_ATTRIBUTE );
+
+        to_attr->TokenId = from_attr->TokenId;
+        to_attr->AuthenticationId = from_attr->AuthenticationId;
+        to_attr->ModifiedId = from_attr->ModifiedId;
+    }
+    if (in->AllocatedAttributes & ALPC_MESSAGE_TOKEN_ATTRIBUTE)
+        current_from_attr += sizeof(ALPC_TOKEN_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_DIRECT_ATTRIBUTE)
+    {
+        const ALPC_DIRECT_ATTR32 *from_attr = (const ALPC_DIRECT_ATTR32 *)current_from_attr;
+        ALPC_DIRECT_ATTR *to_attr = AlpcGetMessageAttribute( attr, ALPC_MESSAGE_DIRECT_ATTRIBUTE );
+
+        to_attr->Event = UlongToHandle( from_attr->Event );
+    }
+    if (in->AllocatedAttributes & ALPC_MESSAGE_DIRECT_ATTRIBUTE)
+        current_from_attr += sizeof(ALPC_DIRECT_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE)
+    {
+        const ALPC_WORK_ON_BEHALF_ATTR32 *from_attr = (const ALPC_WORK_ON_BEHALF_ATTR32 *)current_from_attr;
+        ALPC_WORK_ON_BEHALF_ATTR *to_attr = AlpcGetMessageAttribute( attr, ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE );
+
+        to_attr->Ticket = from_attr->Ticket;
+    }
+
+    *out = attr;
+    return attr;
+}
+
+static inline ALPC_PORT_MESSAGE32 *alpc_port_message_64to32( ALPC_PORT_MESSAGE32 *out,
+                                                             const ALPC_PORT_MESSAGE *in )
+{
+    if (!in) return NULL;
+
+    out->DataLength = in->DataLength;
+    out->TotalLength = sizeof(*out) + in->DataLength;
+    out->Type = in->Type;
+    out->DataInfoOffset = in->DataInfoOffset;
+    out->ClientId.UniqueProcess = HandleToUlong( in->ClientId.UniqueProcess );
+    out->ClientId.UniqueThread = HandleToUlong( in->ClientId.UniqueThread );
+    out->MessageId = in->MessageId;
+    out->ClientViewSize = in->ClientViewSize;
+    memcpy( (unsigned char *)out + sizeof(*out), (const unsigned char *)in + sizeof(*in), in->DataLength );
+    return out;
+}
+
+static inline ALPC_MESSAGE_ATTRIBUTES32 *alpc_port_message_attributes_64to32( ALPC_MESSAGE_ATTRIBUTES32 *out,
+                                                                              ALPC_MESSAGE_ATTRIBUTES *in )
+{
+    unsigned char *current_to_attr;
+
+    if (!in) return NULL;
+
+    out->AllocatedAttributes = in->AllocatedAttributes;
+    out->ValidAttributes = in->ValidAttributes;
+
+    current_to_attr = (unsigned char *)out + sizeof(*out);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_SECURITY_ATTRIBUTE)
+    {
+        const ALPC_SECURITY_ATTR *from_attr = AlpcGetMessageAttribute( in, ALPC_MESSAGE_SECURITY_ATTRIBUTE );
+        ALPC_SECURITY_ATTR32 *to_attr = (ALPC_SECURITY_ATTR32 *)current_to_attr;
+
+        to_attr->Flags = from_attr->Flags;
+        to_attr->ContextHandle = HandleToUlong( from_attr->ContextHandle );
+        if (to_attr->QoSPointer && from_attr->QoS)
+        {
+            SECURITY_QUALITY_OF_SERVICE32 *qos32 = ULongToPtr( to_attr->QoSPointer );
+            qos32->ImpersonationLevel = from_attr->QoS->ImpersonationLevel;
+            qos32->ContextTrackingMode = from_attr->QoS->ContextTrackingMode;
+            qos32->EffectiveOnly = from_attr->QoS->EffectiveOnly;
+        }
+    }
+    if (out->AllocatedAttributes & ALPC_MESSAGE_SECURITY_ATTRIBUTE)
+        current_to_attr += sizeof(ALPC_SECURITY_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_VIEW_ATTRIBUTE)
+    {
+        const ALPC_VIEW_ATTR *from_attr = AlpcGetMessageAttribute( in, ALPC_MESSAGE_VIEW_ATTRIBUTE );
+        ALPC_VIEW_ATTR32 *to_attr = (ALPC_VIEW_ATTR32 *)current_to_attr;
+
+        to_attr->Flags = from_attr->Flags;
+        to_attr->SectionHandle = HandleToUlong( from_attr->SectionHandle );
+        to_attr->ViewBase = PtrToUlong( from_attr->ViewBase );
+        to_attr->ViewSize = from_attr->ViewSize;
+    }
+    if (out->AllocatedAttributes & ALPC_MESSAGE_VIEW_ATTRIBUTE)
+        current_to_attr += sizeof(ALPC_VIEW_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_CONTEXT_ATTRIBUTE)
+    {
+        const ALPC_CONTEXT_ATTR *from_attr = AlpcGetMessageAttribute( in, ALPC_MESSAGE_CONTEXT_ATTRIBUTE );
+        ALPC_CONTEXT_ATTR32 *to_attr = (ALPC_CONTEXT_ATTR32 *)current_to_attr;
+
+        to_attr->PortContext = PtrToUlong( from_attr->PortContext );
+        to_attr->MessageContext = PtrToUlong( from_attr->MessageContext );
+        to_attr->Sequence = from_attr->Sequence;
+        /* Should be from_attr->MessageId. But tests show that it's always 0 on 32-bit */
+        to_attr->MessageId = 0;
+        to_attr->CallbackId = from_attr->CallbackId;
+    }
+    if (out->AllocatedAttributes & ALPC_MESSAGE_CONTEXT_ATTRIBUTE)
+        current_to_attr += sizeof(ALPC_CONTEXT_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_HANDLE_ATTRIBUTE)
+    {
+        const ALPC_HANDLE_ATTR *from_attr = AlpcGetMessageAttribute( in, ALPC_MESSAGE_HANDLE_ATTRIBUTE );
+        ALPC_HANDLE_ATTR32 *to_attr = (ALPC_HANDLE_ATTR32 *)current_to_attr;
+
+        to_attr->Flags = from_attr->Flags;
+        to_attr->Handle = HandleToUlong( from_attr->Handle );
+        to_attr->ObjectType = from_attr->ObjectType;
+        to_attr->DesiredAccess = from_attr->DesiredAccess;
+    }
+    if (out->AllocatedAttributes & ALPC_MESSAGE_HANDLE_ATTRIBUTE)
+        current_to_attr += sizeof(ALPC_HANDLE_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_TOKEN_ATTRIBUTE)
+    {
+        const ALPC_TOKEN_ATTR *from_attr = AlpcGetMessageAttribute( in, ALPC_MESSAGE_TOKEN_ATTRIBUTE );
+        ALPC_TOKEN_ATTR32 *to_attr = (ALPC_TOKEN_ATTR32 *)current_to_attr;
+
+        to_attr->TokenId = from_attr->TokenId;
+        to_attr->AuthenticationId = from_attr->AuthenticationId;
+        to_attr->ModifiedId = from_attr->ModifiedId;
+    }
+    if (out->AllocatedAttributes & ALPC_MESSAGE_TOKEN_ATTRIBUTE)
+        current_to_attr += sizeof(ALPC_TOKEN_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_DIRECT_ATTRIBUTE)
+    {
+        const ALPC_DIRECT_ATTR *from_attr = AlpcGetMessageAttribute( in, ALPC_MESSAGE_DIRECT_ATTRIBUTE );
+        ALPC_DIRECT_ATTR32 *to_attr = (ALPC_DIRECT_ATTR32 *)current_to_attr;
+
+        to_attr->Event = HandleToUlong( from_attr->Event );
+    }
+    if (out->AllocatedAttributes & ALPC_MESSAGE_DIRECT_ATTRIBUTE)
+        current_to_attr += sizeof(ALPC_DIRECT_ATTR32);
+
+    if (in->ValidAttributes & ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE)
+    {
+        const ALPC_WORK_ON_BEHALF_ATTR *from_attr = AlpcGetMessageAttribute( in, ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE );
+        ALPC_WORK_ON_BEHALF_ATTR32 *to_attr = (ALPC_WORK_ON_BEHALF_ATTR32 *)current_to_attr;
+
+        to_attr->Ticket = from_attr->Ticket;
+    }
+
+    return out;
+}
+
 static inline TOKEN_USER *token_user_32to64( TOKEN_USER *out, const TOKEN_USER32 *in )
 {
     out->User.Sid = ULongToPtr( in->User.Sid );
diff --git a/dlls/wow64win/user.c b/dlls/wow64win/user.c
index 103b785591cf..61c2da3e2286 100644
--- a/dlls/wow64win/user.c
+++ b/dlls/wow64win/user.c
@@ -338,13 +338,6 @@ typedef struct
     ULONG  hIconSm;
 } WNDCLASSEXW32;
 
-struct client_menu_name32
-{
-    ULONG nameA;
-    ULONG nameW;
-    ULONG nameUS;
-};
-
 struct win_proc_params32
 {
     ULONG func;
@@ -435,30 +428,6 @@ static MSG32 *msg_64to32( const MSG *msg64, MSG32 *msg32 )
     return msg32;
 }
 
-static struct client_menu_name *client_menu_name_32to64( struct client_menu_name *name,
-                                                         const struct client_menu_name32 *name32 )
-{
-    if (!name32) return NULL;
-    name->nameA = UlongToPtr( name32->nameA );
-    name->nameW = UlongToPtr( name32->nameW );
-    name->nameUS = UlongToPtr( name32->nameUS );
-    return name;
-}
-
-static struct client_menu_name32 *client_menu_name_64to32( const struct client_menu_name *name64,
-                                                           struct client_menu_name32 *name32 )
-{
-    if (name32)
-    {
-        struct client_menu_name32 name;
-        name.nameA = PtrToUlong( name64->nameA );
-        name.nameW = PtrToUlong( name64->nameW );
-        name.nameUS = PtrToUlong( name64->nameUS );
-        memcpy( name32, &name, sizeof(name) );
-    }
-    return name32;
-}
-
 static void win_proc_params_64to32( const struct win_proc_params *src, struct win_proc_params32 *dst )
 {
     struct win_proc_params32 params;
@@ -2371,17 +2340,17 @@ NTSTATUS WINAPI wow64_NtUserGetClassInfoEx( UINT *args )
     HINSTANCE instance = get_ptr( &args );
     UNICODE_STRING32 *name32 = get_ptr( &args );
     WNDCLASSEXW32 *wc32 = get_ptr( &args );
-    struct client_menu_name32 *client_name32 = get_ptr( &args );
+    ULONG *menu_name32 = get_ptr( &args );
     BOOL ansi = get_ulong( &args );
 
-    struct client_menu_name client_name;
+    struct client_menu_name *menu_name;
     UNICODE_STRING name;
     WNDCLASSEXW wc;
     ATOM ret;
 
     wc.cbSize = sizeof(wc);
     if (!(ret = NtUserGetClassInfoEx( instance, unicode_str_32to64( &name, name32 ), &wc,
-                                      &client_name, ansi )))
+                                      &menu_name, ansi )))
         return 0;
 
     wc32->style = wc.style;
@@ -2395,7 +2364,7 @@ NTSTATUS WINAPI wow64_NtUserGetClassInfoEx( UINT *args )
     wc32->lpszMenuName = PtrToUlong( wc.lpszMenuName );
     wc32->lpszClassName = PtrToUlong( wc.lpszClassName );
     wc32->hIconSm = HandleToUlong( wc.hIconSm );
-    client_menu_name_64to32( &client_name, client_name32 );
+    *menu_name32 = PtrToUlong( menu_name );
     return ret;
 }
 
@@ -2779,6 +2748,18 @@ NTSTATUS WINAPI wow64_NtUserGetMessage( UINT *args )
     return ret;
 }
 
+NTSTATUS WINAPI wow64_NtUserGetMessagePos( UINT *args )
+{
+    return NtUserGetMessagePos();
+}
+
+NTSTATUS WINAPI wow64_NtUserSetMessageExtraInfo( UINT *args )
+{
+    LONG lparam = get_ulong( &args );
+
+    return NtUserSetMessageExtraInfo( lparam );
+}
+
 NTSTATUS WINAPI wow64_NtUserGetMouseMovePointsEx( UINT *args )
 {
     UINT size = get_ulong( &args );
@@ -3075,12 +3056,11 @@ NTSTATUS WINAPI wow64_NtUserRegisterClassExWOW( UINT *args )
     const WNDCLASSEXW32 *wc32 = get_ptr( &args );
     UNICODE_STRING32 *name32 = get_ptr( &args );
     UNICODE_STRING32 *version32 = get_ptr( &args );
-    struct client_menu_name32 *client_name32 = get_ptr( &args );
+    struct client_menu_name *menu_name = get_ptr( &args );
     DWORD fnid = get_ulong( &args );
     DWORD flags = get_ulong( &args );
     DWORD *wow = get_ptr( &args );
 
-    struct client_menu_name client_name;
     UNICODE_STRING name, version;
     WNDCLASSEXW wc;
 
@@ -3103,11 +3083,9 @@ NTSTATUS WINAPI wow64_NtUserRegisterClassExWOW( UINT *args )
     wc.lpszClassName = UlongToPtr( wc32->lpszClassName );
     wc.hIconSm = LongToHandle( wc32->hIconSm );
 
-    return NtUserRegisterClassExWOW( &wc,
-                                     unicode_str_32to64( &name, name32 ),
+    return NtUserRegisterClassExWOW( &wc, unicode_str_32to64( &name, name32 ),
                                      unicode_str_32to64( &version, version32 ),
-                                     client_menu_name_32to64( &client_name, client_name32 ),
-                                     fnid, flags, wow );
+                                     menu_name, fnid, flags, wow );
 }
 
 NTSTATUS WINAPI wow64_NtUserGetRegisteredRawInputDevices( UINT *args )
@@ -4292,16 +4270,6 @@ NTSTATUS WINAPI wow64_NtUserSetClassLongPtr( UINT *args )
     LONG_PTR newval = get_ulong( &args );
     BOOL ansi = get_ulong( &args );
 
-    if (offset == GCLP_MENUNAME)
-    {
-        struct client_menu_name menu_name;
-        struct client_menu_name32 *menu_name32 = UlongToPtr( newval );
-        NtUserSetClassLongPtr( hwnd, offset,
-                               (UINT_PTR)client_menu_name_32to64( &menu_name, menu_name32 ), ansi );
-        client_menu_name_64to32( &menu_name, menu_name32 );
-        return 0;
-    }
-
     return NtUserSetClassLongPtr( hwnd, offset, newval, ansi );
 }
 
@@ -5093,14 +5061,14 @@ NTSTATUS WINAPI wow64_NtUserUnregisterClass( UINT *args )
 {
     UNICODE_STRING32 *name32 = get_ptr( &args );
     HINSTANCE instance = get_ptr( &args );
-    struct client_menu_name32 *menu_name32 = get_ptr( &args );
+    ULONG *menu_name32 = get_ptr( &args );
 
+    struct client_menu_name *menu_name;
     UNICODE_STRING name;
-    struct client_menu_name menu_name;
     BOOL ret;
 
     ret = NtUserUnregisterClass( unicode_str_32to64( &name, name32 ), instance, &menu_name );
-    if (ret) client_menu_name_64to32( &menu_name, menu_name32 );
+    if (ret) *menu_name32 = PtrToUlong( menu_name );
     return ret;
 }
 
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index f5580cab4e3a..b2601c6e4af0 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -3022,6 +3022,7 @@ int WINAPI select( int count, fd_set *read_ptr, fd_set *write_ptr,
 
     status = NtDeviceIoControlFile( (HANDLE)poll_socket, sync_event, NULL, NULL, &io,
                                     IOCTL_AFD_POLL, params, params_size, params, params_size );
+    if (status == STATUS_NOT_SUPPORTED) status = STATUS_INVALID_HANDLE;
     if (status == STATUS_PENDING)
     {
         if (wait_event_alertable( sync_event ) == WAIT_FAILED)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index d861f303367f..e67e50907b01 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -4360,6 +4360,8 @@ static void test_select(void)
     unsigned int apc_count;
     unsigned int maxfd, i;
     char *page_pair;
+    char path[MAX_PATH];
+    HANDLE file, hdup;
 
     fdRead = socket(AF_INET, SOCK_STREAM, 0);
     ok( (fdRead != INVALID_SOCKET), "socket failed unexpectedly: %d\n", WSAGetLastError() );
@@ -4459,8 +4461,68 @@ static void test_select(void)
     maxfd = fdRead;
     if(fdWrite > maxfd) maxfd = fdWrite;
 
+    GetSystemWindowsDirectoryA(path, ARRAY_SIZE(path));
+    strcat(path, "\\system.ini");
+
+    file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0x0, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "failed to open file, error %lu\n", GetLastError());
+
+    if ((SOCKET)file > maxfd) maxfd = (SOCKET)file;
+    ret = DuplicateHandle(GetCurrentProcess(), (HANDLE)fdRead, GetCurrentProcess(), &hdup, 0, FALSE, DUPLICATE_SAME_ACCESS);
+    ok(ret, "got %d.\n", ret);
+
+    /* Test with valid but non socket handle which also supports some ioctls. */
+    FD_ZERO_ALL();
+    FD_SET((SOCKET)file, &readfds);
+    SetLastError(0);
+    ret = select(maxfd + 1, &readfds, &writefds, &exceptfds, &select_timeout);
+    ok ( (ret == SOCKET_ERROR), "expected SOCKET_ERROR, got %i\n", ret);
+    ok ( WSAGetLastError() == WSAENOTSOCK, "expected WSAENOTSOCK, got %i\n", WSAGetLastError());
+    ok ( FD_ISSET((SOCKET)file, &readfds), "FD should be set\n");
+
+    FD_ZERO(&readfds);
+    FD_SET(fdRead, &readfds);
+    FD_SET(fdRead, &exceptfds);
+    FD_SET((SOCKET)file, &writefds);
+    SetLastError(0);
+    ret = select(maxfd + 1, &readfds, &writefds, &exceptfds, &select_timeout);
+    ok ( (ret == SOCKET_ERROR), "expected SOCKET_ERROR, got %i\n", ret);
+    ok ( WSAGetLastError() == WSAENOTSOCK, "expected WSAENOTSOCK, got %i\n", WSAGetLastError());
+
+    FD_ZERO_ALL();
+    FD_SET(fdRead, &readfds);
+    FD_SET(fdWrite, &writefds);
+    FD_SET((SOCKET)file, &exceptfds);
+    SetLastError(0);
+    ret = select(maxfd + 1, &readfds, &writefds, &exceptfds, &select_timeout);
+    ok ( (ret == SOCKET_ERROR), "expected SOCKET_ERROR, got %i\n", ret);
+    ok ( WSAGetLastError() == WSAENOTSOCK, "expected WSAENOTSOCK, got %i\n", WSAGetLastError());
+
+    /* Test with duplicated handle of valid socket. */
+    FD_ZERO_ALL();
+    FD_SET((SOCKET)hdup, &readfds);
+    ret = select(maxfd + 1, &readfds, &writefds, &exceptfds, &select_timeout);
+    ok( !ret, "select returned %d\n", ret );
+
     FD_ZERO(&readfds);
     FD_SET(fdRead, &readfds);
+    FD_SET(fdRead, &exceptfds);
+    FD_SET((SOCKET)hdup, &writefds);
+    ret = select(maxfd + 1, &readfds, &writefds, &exceptfds, &select_timeout);
+    ok( ret == 1, "select returned %d\n", ret );
+
+    FD_ZERO_ALL();
+    FD_SET(fdRead, &readfds);
+    FD_SET(fdWrite, &writefds);
+    FD_SET((SOCKET)hdup, &exceptfds);
+    ret = select(maxfd + 1, &readfds, &writefds, &exceptfds, &select_timeout);
+    ok( ret == 1, "select returned %d\n", ret );
+
+    CloseHandle(hdup);
+    CloseHandle(file);
+
+    FD_ZERO_ALL();
+    FD_SET(fdRead, &readfds);
     apc_count = 0;
     ret = QueueUserAPC(apc_func, GetCurrentThread(), (ULONG_PTR)&apc_count);
     ok(ret, "QueueUserAPC returned %d\n", ret);
diff --git a/include/Makefile.in b/include/Makefile.in
index 2a92170c459e..47df75fb59e1 100644
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -108,6 +108,7 @@ SOURCES = \
 	cryptdlg.h \
 	cryptuiapi.h \
 	cryptxml.h \
+	ctffunc.idl \
 	ctfutb.idl \
 	ctxtcall.idl \
 	custcntl.h \
@@ -730,6 +731,8 @@ SOURCES = \
 	natupnp.idl \
 	nb30.h \
 	ncrypt.h \
+	ndattrib.idl \
+	ndfapi.h \
 	ndrtypes.h \
 	netcfgx.idl \
 	netcon.idl \
@@ -861,6 +864,7 @@ SOURCES = \
 	rstvw.idl \
 	rstxsc.idl \
 	rtlsupportapi.h \
+	rtscom.idl \
 	rtutils.h \
 	rtworkq.idl \
 	sal.h \
@@ -941,7 +945,7 @@ SOURCES = \
 	tlogstg.idl \
 	tmschema.h \
 	tom.idl \
-	tpcshrd.h \
+	tpcshrd.idl \
 	traffic.h \
 	transact.idl \
 	trnjoi.idl \
diff --git a/include/comsvcs.idl b/include/comsvcs.idl
index 902c78b05c84..614b9ee43f3f 100644
--- a/include/comsvcs.idl
+++ b/include/comsvcs.idl
@@ -23,6 +23,8 @@ import "oaidl.idl";
 #define threading(model)
 #endif
 
+cpp_quote("EXTERN_C const GUID IID_IEnterActivityWithNoLock;")
+
 typedef DWORD_PTR INSTID;
 typedef DWORD_PTR RESID;
 typedef DWORD     RESOURCERATING;
diff --git a/include/ctffunc.idl b/include/ctffunc.idl
new file mode 100644
index 000000000000..c308c5377c7a
--- /dev/null
+++ b/include/ctffunc.idl
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2026 Paul Gofman 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 DO_NO_IMPORTS
+import "oaidl.idl";
+import "msctf.idl";
+#endif
+
+interface IEnumTfCandidates;
+
+[
+  object,
+  uuid(581f317e-fd9d-443f-b972-ed00467c5d40),
+  pointer_default(unique)
+]
+interface ITfCandidateString : IUnknown
+{
+    HRESULT GetString([out] BSTR *bstr);
+    HRESULT GetIndex([out] ULONG *index);
+}
+
+[
+  object,
+  uuid(defb1926-6c80-4ce8-87d4-d6b72b812bde),
+  pointer_default(unique)
+]
+interface IEnumTfCandidates : IUnknown
+{
+    HRESULT Clone([out] IEnumTfCandidates **ret_enum);
+    HRESULT Next([in] ULONG count, [out] ITfCandidateString **cand, [out] ULONG *fetched);
+    HRESULT Reset();
+    HRESULT Skip([in] ULONG count);
+}
+
+typedef [uuid(baa898f2-0207-4643-92ca-f3f7b0cf6f80)] enum
+{
+    CAND_FINALIZED = 0x0,
+    CAND_SELECTED  = 0x1,
+    CAND_CANCELED  = 0x2,
+} TfCandidateResult;
+
+[
+  object,
+  uuid(a3ad50fb-9bdb-49e3-a843-6c76520fbf5d),
+  pointer_default(unique)
+]
+interface ITfCandidateList : IUnknown
+{
+    HRESULT EnumCandidates([out] IEnumTfCandidates **ret_enum);
+    HRESULT GetCandidate([in] ULONG index, [out] ITfCandidateString **cand);
+    HRESULT GetCandidateNum([out] ULONG *count);
+    HRESULT SetResult([in] ULONG nIndex, [in] TfCandidateResult imcr);
+}
+
+[
+  object,
+  uuid(4cea93c0-0a58-11d3-8df0-00105a2799b5),
+  pointer_default(unique)
+]
+interface ITfFnReconversion : ITfFunction
+{
+    HRESULT QueryRange([in] ITfRange *range, ITfRange **new_range, BOOL *convertable);
+    HRESULT GetReconversion(ITfRange *range, ITfCandidateList **cand_list);
+    HRESULT Reconvert([in] ITfRange *range);
+}
diff --git a/include/msctf.idl b/include/msctf.idl
index 6b0c84bdd531..a1c40e944e4e 100644
--- a/include/msctf.idl
+++ b/include/msctf.idl
@@ -172,6 +172,16 @@ typedef [uuid(d678c645-eb6a-45c9-b4ee-0f3e3a991348)] struct TF_PROPERTYVAL
     VARIANT varValue;
 } TF_PROPERTYVAL;
 
+[
+  object,
+  uuid(db593490-098f-11d3-8df0-00105a2799b5),
+  pointer_default(unique)
+]
+interface ITfFunction : IUnknown
+{
+    HRESULT GetDisplayName([out] BSTR *name);
+}
+
 [
     object,
     uuid(101d6610-0990-11d3-8df0-00105a2799b5),
diff --git a/include/msvcrt/intrin.h b/include/msvcrt/intrin.h
index 2a1177aef963..d4758df865f5 100644
--- a/include/msvcrt/intrin.h
+++ b/include/msvcrt/intrin.h
@@ -19,7 +19,18 @@ extern "C" {
 # define __has_builtin(x) 0
 #endif
 
-#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__))
+#if defined(__aarch64__) || defined(__arm64ec__)
+
+unsigned char    __ldar8(const volatile unsigned char *);
+unsigned short   __ldar16(const volatile unsigned short *);
+unsigned int     __ldar32(const volatile unsigned int *);
+unsigned __int64 __ldar64(const volatile unsigned __int64 *);
+void __stlr8(volatile unsigned char *, unsigned char);
+void __stlr16(volatile unsigned short *, unsigned short);
+void __stlr32(volatile unsigned int *, unsigned int);
+void __stlr64(volatile unsigned __int64 *, unsigned __int64);
+
+#elif defined(__i386__) || defined(__x86_64__)
 
 #if __has_builtin(__cpuidex) || (defined(_MSC_VER) && !defined(__clang__))
 void __cpuidex(int info[4], int ax, int cx);
@@ -107,36 +118,200 @@ typedef enum _tag_ARMINTR_BARRIER_TYPE
 } _ARMINTR_BARRIER_TYPE;
 #endif
 
-#if defined(_MSC_VER) && (defined(__arm__) || defined(__aarch64__) || defined(__arm64ec__))
+#ifdef _MSC_VER
 
-void __dmb(unsigned int);
+unsigned char _BitScanForward(unsigned long*,unsigned long);
+unsigned char _BitScanReverse(unsigned long*,unsigned long);
+void          _ReadBarrier(void);
+void          _ReadWriteBarrier(void);
+void          _WriteBarrier(void);
 
-#pragma intrinsic(__dmb)
+long          _InterlockedAnd(long volatile *, long);
+char          _InterlockedAnd8(char volatile *, char);
+short         _InterlockedAnd16(short volatile *, short);
+long          _InterlockedCompareExchange(long volatile *, long, long);
+char          _InterlockedCompareExchange8(char volatile *, char, char);
+short         _InterlockedCompareExchange16(short volatile *, short, short);
+__int64       _InterlockedCompareExchange64(__int64 volatile *, __int64, __int64);
+void *        _InterlockedCompareExchangePointer(void * volatile *, void *, void *);
+long          _InterlockedDecrement(long volatile *);
+short         _InterlockedDecrement16(short volatile *);
+long          _InterlockedExchange(long volatile *, long);
+char          _InterlockedExchange8(char volatile *, char);
+short         _InterlockedExchange16(short volatile *, short);
+long          _InterlockedExchangeAdd(long volatile *, long);
+char          _InterlockedExchangeAdd8(char volatile *, char);
+short         _InterlockedExchangeAdd16(short volatile *, short);
+void *        _InterlockedExchangePointer(void * volatile *, void *);
+long          _InterlockedIncrement(long volatile *);
+short         _InterlockedIncrement16(short volatile *);
+long          _InterlockedOr(long volatile *, long);
+char          _InterlockedOr8(char volatile *, char);
+short         _InterlockedOr16(short volatile *, short);
+long          _InterlockedXor(long volatile *, long);
+char          _InterlockedXor8(char volatile *, char);
+short         _InterlockedXor16(short volatile *, short);
 
+#ifndef __i386__
+__int64       _InterlockedAnd64(__int64 volatile *, __int64);
+__int64       _InterlockedDecrement64(__int64 volatile *);
+__int64       _InterlockedExchange64(__int64 volatile *, __int64);
+__int64       _InterlockedExchangeAdd64(__int64 volatile *, __int64);
+__int64       _InterlockedIncrement64(__int64 volatile *);
+__int64       _InterlockedOr64(__int64 volatile *, __int64);
+__int64       _InterlockedXor64(__int64 volatile *, __int64);
 #endif
 
-#if defined(_MSC_VER) && (defined(__aarch64__) || defined(__arm64ec__))
-
-unsigned __int64 __getReg(int);
-#pragma intrinsic(__getReg)
-
-#endif
+#if defined(__arm__) || defined(__aarch64__) || defined(__arm64ec__)
+long          _InterlockedAdd(long volatile *, long);
+long          _InterlockedAdd_acq(long volatile *, long);
+long          _InterlockedAdd_nf(long volatile *, long);
+long          _InterlockedAdd_rel(long volatile *, long);
+__int64       _InterlockedAdd64(__int64 volatile *, __int64);
+__int64       _InterlockedAdd64_acq(__int64 volatile *, __int64);
+__int64       _InterlockedAdd64_nf(__int64 volatile *, __int64);
+__int64       _InterlockedAdd64_rel(__int64 volatile *, __int64);
+long          _InterlockedAnd_acq(long volatile *, long);
+long          _InterlockedAnd_nf(long volatile *, long);
+long          _InterlockedAnd_rel(long volatile *, long);
+char          _InterlockedAnd8_acq(char volatile *, char);
+char          _InterlockedAnd8_nf(char volatile *, char);
+char          _InterlockedAnd8_rel(char volatile *, char);
+short         _InterlockedAnd16_acq(short volatile *, short);
+short         _InterlockedAnd16_nf(short volatile *, short);
+short         _InterlockedAnd16_rel(short volatile *, short);
+__int64       _InterlockedAnd64_acq(__int64 volatile *, __int64);
+__int64       _InterlockedAnd64_nf(__int64 volatile *, __int64);
+__int64       _InterlockedAnd64_rel(__int64 volatile *, __int64);
+long          _InterlockedCompareExchange_acq(long volatile *, long, long);
+long          _InterlockedCompareExchange_nf(long volatile *, long, long);
+long          _InterlockedCompareExchange_rel(long volatile *, long, long);
+char          _InterlockedCompareExchange8_acq(char volatile *, char, char);
+char          _InterlockedCompareExchange8_nf(char volatile *, char, char);
+char          _InterlockedCompareExchange8_rel(char volatile *, char, char);
+short         _InterlockedCompareExchange16_acq(short volatile *, short, short);
+short         _InterlockedCompareExchange16_nf(short volatile *, short, short);
+short         _InterlockedCompareExchange16_rel(short volatile *, short, short);
+__int64       _InterlockedCompareExchange64_acq(__int64 volatile *, __int64, __int64);
+__int64       _InterlockedCompareExchange64_nf(__int64 volatile *, __int64, __int64);
+__int64       _InterlockedCompareExchange64_rel(__int64 volatile *, __int64, __int64);
+void *        _InterlockedCompareExchangePointer_acq(void * volatile *, void *, void *);
+void *        _InterlockedCompareExchangePointer_nf(void * volatile *, void *, void *);
+void *        _InterlockedCompareExchangePointer_rel(void * volatile *, void *, void *);
+long          _InterlockedDecrement_acq(long volatile *);
+long          _InterlockedDecrement_nf(long volatile *);
+long          _InterlockedDecrement_rel(long volatile *);
+short         _InterlockedDecrement16_acq(short volatile *);
+short         _InterlockedDecrement16_nf(short volatile *);
+short         _InterlockedDecrement16_rel(short volatile *);
+__int64       _InterlockedDecrement64_acq(__int64 volatile *);
+__int64       _InterlockedDecrement64_nf(__int64 volatile *);
+__int64       _InterlockedDecrement64_rel(__int64 volatile *);
+long          _InterlockedExchange_acq(long volatile *, long);
+long          _InterlockedExchange_nf(long volatile *, long);
+long          _InterlockedExchange_rel(long volatile *, long);
+char          _InterlockedExchange8_acq(char volatile *, char);
+char          _InterlockedExchange8_nf(char volatile *, char);
+char          _InterlockedExchange8_rel(char volatile *, char);
+short         _InterlockedExchange16_acq(short volatile *, short);
+short         _InterlockedExchange16_nf(short volatile *, short);
+short         _InterlockedExchange16_rel(short volatile *, short);
+__int64       _InterlockedExchange64_acq(__int64 volatile *, __int64);
+__int64       _InterlockedExchange64_nf(__int64 volatile *, __int64);
+__int64       _InterlockedExchange64_rel(__int64 volatile *, __int64);
+void *        _InterlockedExchangePointer_acq(void * volatile *, void *);
+void *        _InterlockedExchangePointer_nf(void * volatile *, void *);
+void *        _InterlockedExchangePointer_rel(void * volatile *, void *);
+long          _InterlockedExchangeAdd_acq(long volatile *, long);
+long          _InterlockedExchangeAdd_nf(long volatile *, long);
+long          _InterlockedExchangeAdd_rel(long volatile *, long);
+char          _InterlockedExchangeAdd8_acq(char volatile *, char);
+char          _InterlockedExchangeAdd8_nf(char volatile *, char);
+char          _InterlockedExchangeAdd8_rel(char volatile *, char);
+short         _InterlockedExchangeAdd16_acq(short volatile *, short);
+short         _InterlockedExchangeAdd16_nf(short volatile *, short);
+short         _InterlockedExchangeAdd16_rel(short volatile *, short);
+__int64       _InterlockedExchangeAdd64_acq(__int64 volatile *, __int64);
+__int64       _InterlockedExchangeAdd64_nf(__int64 volatile *, __int64);
+__int64       _InterlockedExchangeAdd64_rel(__int64 volatile *, __int64);
+long          _InterlockedIncrement_acq(long volatile *);
+long          _InterlockedIncrement_nf(long volatile *);
+long          _InterlockedIncrement_rel(long volatile *);
+short         _InterlockedIncrement16_acq(short volatile *);
+short         _InterlockedIncrement16_nf(short volatile *);
+short         _InterlockedIncrement16_rel(short volatile *);
+__int64       _InterlockedIncrement64_acq(__int64 volatile *);
+__int64       _InterlockedIncrement64_nf(__int64 volatile *);
+__int64       _InterlockedIncrement64_rel(__int64 volatile *);
+long          _InterlockedOr_acq(long volatile *, long);
+long          _InterlockedOr_nf(long volatile *, long);
+long          _InterlockedOr_rel(long volatile *, long);
+char          _InterlockedOr8_acq(char volatile *, char);
+char          _InterlockedOr8_nf(char volatile *, char);
+char          _InterlockedOr8_rel(char volatile *, char);
+short         _InterlockedOr16_acq(short volatile *, short);
+short         _InterlockedOr16_nf(short volatile *, short);
+short         _InterlockedOr16_rel(short volatile *, short);
+__int64       _InterlockedOr64_acq(__int64 volatile *, __int64);
+__int64       _InterlockedOr64_nf(__int64 volatile *, __int64);
+__int64       _InterlockedOr64_rel(__int64 volatile *, __int64);
+long          _InterlockedXor_acq(long volatile *, long);
+long          _InterlockedXor_nf(long volatile *, long);
+long          _InterlockedXor_rel(long volatile *, long);
+char          _InterlockedXor8_acq(char volatile *, char);
+char          _InterlockedXor8_nf(char volatile *, char);
+char          _InterlockedXor8_rel(char volatile *, char);
+short         _InterlockedXor16_acq(short volatile *, short);
+short         _InterlockedXor16_nf(short volatile *, short);
+short         _InterlockedXor16_rel(short volatile *, short);
+__int64       _InterlockedXor64_acq(__int64 volatile *, __int64);
+__int64       _InterlockedXor64_nf(__int64 volatile *, __int64);
+__int64       _InterlockedXor64_rel(__int64 volatile *, __int64);
 
-#if defined(_MSC_VER)
-unsigned char _BitScanForward(unsigned long*,unsigned long);
-unsigned char _BitScanReverse(unsigned long*,unsigned long);
+void __dmb(unsigned int);
+#pragma intrinsic(__dmb)
 #endif
 
-#if defined(_MSC_VER) && (defined(__x86_64__) || defined(__aarch64__))
+#if defined(__x86_64__) || defined(__aarch64__)
 unsigned char _BitScanForward64(unsigned long*,unsigned __int64);
 unsigned char _BitScanReverse64(unsigned long*,unsigned __int64);
+unsigned char _InterlockedCompareExchange128(__int64 volatile * , __int64, __int64, __int64 *);
 #endif
 
-#if defined(_MSC_VER) && defined(__x86_64__)
+#if defined(__aarch64__) || defined(__arm64ec__)
+unsigned char _InterlockedCompareExchange128_acq(__int64 volatile *, __int64, __int64, __int64 *);
+unsigned char _InterlockedCompareExchange128_nf(__int64 volatile *, __int64, __int64, __int64 *);
+unsigned char _InterlockedCompareExchange128_rel(__int64 volatile *, __int64, __int64, __int64 *);
+
+unsigned __int64 __getReg(int);
+#pragma intrinsic(__getReg)
+#endif
+
+#if defined(__x86_64__) && !defined(__arm64ec__)
+long          _InterlockedAnd_np(long volatile *, long);
+char          _InterlockedAnd8_np(char volatile *, char);
+short         _InterlockedAnd16_np(short volatile *, short);
+__int64       _InterlockedAnd64_np(__int64 volatile *, __int64);
+long          _InterlockedCompareExchange_np(long volatile *, long, long);
+short         _InterlockedCompareExchange16_np(short volatile *, short, short);
+__int64       _InterlockedCompareExchange64_np(__int64 volatile *, __int64, __int64);
+unsigned char _InterlockedCompareExchange128_np(__int64 volatile *, __int64, __int64, __int64 *);
+void *        _InterlockedCompareExchangePointer_np(void * volatile *, void *, void *);
+long          _InterlockedOr_np(long volatile *, long);
+char          _InterlockedOr8_np(char volatile *, char);
+short         _InterlockedOr16_np(short volatile *, short);
+__int64       _InterlockedOr64_np(__int64 volatile *, __int64);
+long          _InterlockedXor_np(long volatile *, long);
+char          _InterlockedXor8_np(char volatile *, char);
+short         _InterlockedXor16_np(short volatile *, short);
+__int64       _InterlockedXor64_np(__int64 volatile *, __int64);
+
 unsigned __int64 __shiftright128(unsigned __int64, unsigned __int64, unsigned char);
 unsigned __int64 _umul128(unsigned __int64, unsigned __int64, unsigned __int64*);
 #endif
 
+#endif  /* _MSC_VER */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/ndattrib.idl b/include/ndattrib.idl
new file mode 100644
index 000000000000..444bf8fbbac3
--- /dev/null
+++ b/include/ndattrib.idl
@@ -0,0 +1,98 @@
+/*
+* Copyright 2026 Alistair Leslie-Hughes
+ *
+ * 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
+ */
+
+import "unknwn.idl";
+import "objidl.idl";
+import "oaidl.idl";
+
+typedef [v1_enum] enum
+{
+    UIT_INVALID,
+    UIT_NONE,
+    UIT_SHELL_COMMAND,
+    UIT_HELP_PANE,
+    UIT_DUI
+} UI_INFO_TYPE;
+
+typedef [v1_enum] enum tagREPAIR_SCOPE
+{
+    RS_SYSTEM,
+    RS_USER,
+    RS_APPLICATION,
+    RS_PROCESS
+} REPAIR_SCOPE;
+
+cpp_quote("#define PREPAIR_SCOPE  *REPAIR_SCOPE")
+
+typedef [v1_enum] enum
+{
+    RR_NOROLLBACK,
+    RR_ROLLBACK,
+    RR_NORISK
+} REPAIR_RISK;
+
+typedef struct tagShellCommandInfo
+{
+    [string] LPWSTR pwszOperation;
+    [string] LPWSTR pwszFile;
+    [string] LPWSTR pwszParameters;
+    [string] LPWSTR pwszDirectory;
+    ULONG nShowCmd;
+} ShellCommandInfo, *PShellCommandInfo;
+
+typedef struct tagUiInfo
+{
+    UI_INFO_TYPE type;
+    [switch_type(UI_INFO_TYPE), switch_is(type) ] union
+    {
+        [ case(UIT_NONE) ]          LPWSTR pwzNull;
+        [ case(UIT_SHELL_COMMAND) ] ShellCommandInfo ShellInfo;
+        [ case(UIT_HELP_PANE) ]     LPWSTR pwzHelpUrl;
+        [ case(UIT_DUI) ]           LPWSTR pwzDui;
+    };
+} UiInfo, *PUiInfo;
+
+typedef struct tagRepairInfo
+{
+    GUID guid;
+    [string] LPWSTR pwszClassName;
+    [string] LPWSTR pwszDescription;
+    DWORD sidType;
+    long cost;
+    ULONG flags;
+    REPAIR_SCOPE scope;
+    REPAIR_RISK risk;
+    UiInfo UiInfo;
+    int rootCauseIndex;
+} RepairInfo, *PRepairInfo;
+
+typedef struct tagRepairInfoEx
+{
+    RepairInfo repair;
+    USHORT repairRank;
+} RepairInfoEx, *PRepairInfoEx;
+
+typedef struct tagRootCauseInfo
+{
+    [string] LPWSTR pwszDescription;
+    GUID rootCauseID;
+    DWORD rootCauseFlags;
+    GUID networkInterfaceID;
+    [size_is(repairCount)] RepairInfoEx *pRepairs;
+    USHORT repairCount;
+} RootCauseInfo, *PRootCauseInfo;
diff --git a/include/ndfapi.h b/include/ndfapi.h
new file mode 100644
index 000000000000..82ec36e844eb
--- /dev/null
+++ b/include/ndfapi.h
@@ -0,0 +1,39 @@
+/*
+* Copyright 2026 Alistair Leslie-Hughes
+ *
+ * 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 __NDFAPI_H__
+#define __NDFAPI_H__
+
+#include "ndattrib.h"
+
+typedef PVOID NDFHANDLE;
+
+STDAPI NdfCloseIncident(NDFHANDLE handle);
+STDAPI NdfCreateConnectivityIncident(NDFHANDLE *handle);
+STDAPI NdfCreateWebIncident(LPCWSTR url, NDFHANDLE *handle);
+STDAPI NdfCreateWinSockIncident(SOCKET sock, LPCWSTR host, USHORT port, LPCWSTR appId, SID *userId, NDFHANDLE *handle);
+
+#ifdef __cplusplus
+STDAPI NdfDiagnoseIncident(NDFHANDLE Handle, ULONG *RootCauseCount, RootCauseInfo **RootCauses, DWORD dwWait=INFINITE, DWORD dwFlags=0);
+#else
+STDAPI NdfDiagnoseIncident(NDFHANDLE Handle, ULONG *RootCauseCount, RootCauseInfo **RootCauses, DWORD dwWait, DWORD dwFlags);
+#endif
+
+STDAPI NdfExecuteDiagnosis(NDFHANDLE handle, HWND hwnd);
+
+
+#endif
diff --git a/include/ntuser.h b/include/ntuser.h
index ea6735245365..2bbf77a4cf1e 100644
--- a/include/ntuser.h
+++ b/include/ntuser.h
@@ -121,20 +121,14 @@ static inline NTSTATUS KeUserDispatchCallback( const struct dispatch_callback_pa
 /* TEB thread info, not compatible with Windows */
 struct ntuser_thread_info
 {
-    DWORD          message_time;      /* value for GetMessageTime */
-    DWORD          message_pos;       /* value for GetMessagePos */
-    UINT64         message_extra;     /* value for GetMessageExtraInfo */
     INPUT_MESSAGE_SOURCE msg_source;  /* Message source for current message */
     WORD           recursion_count;   /* SendMessage recursion counter */
-    UINT           receive_flags;     /* currently received message flags */
-    UINT           top_window;        /* desktop window */
-    UINT           msg_window;        /* HWND_MESSAGE parent window */
     UINT           dpi_context;       /* DPI awareness context */
-    UINT           default_imc;       /* default input context */
     UINT64         client_imm;        /* client IMM thread info */
     UINT64         wmchar_data;       /* client data for WM_CHAR mappings */
 };
 
+#ifndef WINE_UNIX_LIB
 static inline struct ntuser_thread_info *NtUserGetThreadInfo(void)
 {
 #ifndef _WIN64
@@ -146,6 +140,7 @@ static inline struct ntuser_thread_info *NtUserGetThreadInfo(void)
 #endif
     return (struct ntuser_thread_info *)NtCurrentTeb()->Win32ClientInfo;
 }
+#endif
 
 /* NtUserCallEnumDisplayMonitor params */
 struct enum_display_monitor_params
@@ -562,12 +557,8 @@ struct cursoricon_desc
     HRSRC rsrc;
 };
 
-struct client_menu_name
-{
-    char  *nameA;
-    WCHAR *nameW;
-    UNICODE_STRING *nameUS;
-};
+/* WCHAR[] unicode name, followed by char[] ansi name */
+struct client_menu_name;
 
 /* NtUserGetClipboardData params, not compatible with Windows */
 struct get_clipboard_params
@@ -667,10 +658,7 @@ enum wine_internal_message
 #define IMN_WINE_SET_COMP_STRING  0x0010
 
 /* not compatible with Windows */
-#define GWLP_FNID_INTERNAL (-1000)
-#define MAKE_FNID(off, len) (((off) << 8) + len)
-#define FNID_OFF(fnid) ((fnid) >> 8)
-#define FNID_LEN(fnid) ((fnid) & 0xff)
+#define MAKE_FNID(index) ((WORD)(0x8000 | (index)))
 
 /* builtin IME driver calls */
 enum wine_ime_call
@@ -849,7 +837,7 @@ W32KAPI ULONG   WINAPI NtUserGetAtomName( ATOM atom, UNICODE_STRING *name );
 W32KAPI UINT    WINAPI NtUserGetCaretBlinkTime(void);
 W32KAPI BOOL    WINAPI NtUserGetCaretPos( POINT *point );
 W32KAPI ATOM    WINAPI NtUserGetClassInfoEx( HINSTANCE instance, UNICODE_STRING *name, WNDCLASSEXW *wc,
-                                             struct client_menu_name *menu_name, BOOL ansi );
+                                             struct client_menu_name **menu_name, BOOL ansi );
 W32KAPI INT     WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name );
 W32KAPI BOOL    WINAPI NtUserGetClipCursor( RECT *rect );
 W32KAPI HANDLE  WINAPI NtUserGetClipboardData( UINT format, struct get_clipboard_params *params );
@@ -885,6 +873,7 @@ W32KAPI BOOL    WINAPI NtUserGetLayeredWindowAttributes( HWND hwnd, COLORREF *ke
 W32KAPI BOOL    WINAPI NtUserGetMenuBarInfo( HWND hwnd, LONG id, LONG item, MENUBARINFO *info );
 W32KAPI BOOL    WINAPI NtUserGetMenuItemRect( HWND hwnd, HMENU menu, UINT item, RECT *rect );
 W32KAPI BOOL    WINAPI NtUserGetMessage( MSG *msg, HWND hwnd, UINT first, UINT last );
+W32KAPI DWORD   WINAPI NtUserGetMessagePos(void);
 W32KAPI int     WINAPI NtUserGetMouseMovePointsEx( UINT size, MOUSEMOVEPOINT *ptin, MOUSEMOVEPOINT *ptout,
                                                    int count, DWORD resolution );
 W32KAPI BOOL    WINAPI NtUserGetObjectInformation( HANDLE handle, INT index, void *info,
@@ -964,8 +953,7 @@ W32KAPI HWND    WINAPI NtUserRealChildWindowFromPoint( HWND parent, LONG x, LONG
 W32KAPI UINT    WINAPI NtUserRealizePalette( HDC hdc );
 W32KAPI BOOL    WINAPI NtUserRedrawWindow( HWND hwnd, const RECT *rect, HRGN hrgn, UINT flags );
 W32KAPI ATOM    WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *name, UNICODE_STRING *version,
-                                                 struct client_menu_name *client_menu_name, DWORD fnid, DWORD flags,
-                                                 DWORD *wow );
+                                                 struct client_menu_name *menu_name, DWORD fnid, DWORD flags, DWORD *wow );
 W32KAPI BOOL    WINAPI NtUserRegisterHotKey( HWND hwnd, INT id, UINT modifiers, UINT vk );
 W32KAPI BOOL    WINAPI NtUserRegisterRawInputDevices( const RAWINPUTDEVICE *devices, UINT device_count, UINT size );
 W32KAPI BOOL    WINAPI NtUserRegisterTouchPadCapable( BOOL capable );
@@ -1006,6 +994,7 @@ W32KAPI BOOL    WINAPI NtUserSetLayeredWindowAttributes( HWND hwnd, COLORREF key
 W32KAPI BOOL    WINAPI NtUserSetMenu( HWND hwnd, HMENU menu );
 W32KAPI BOOL    WINAPI NtUserSetMenuContextHelpId( HMENU handle, DWORD id );
 W32KAPI BOOL    WINAPI NtUserSetMenuDefaultItem( HMENU handle, UINT item, UINT bypos );
+W32KAPI LPARAM  WINAPI NtUserSetMessageExtraInfo( LPARAM lparam );
 W32KAPI BOOL    WINAPI NtUserSetObjectInformation( HANDLE handle, INT index, void *info, DWORD len );
 W32KAPI HWND    WINAPI NtUserSetParent( HWND hwnd, HWND parent );
 W32KAPI BOOL    WINAPI NtUserSetProcessDefaultLayout( ULONG layout );
@@ -1055,8 +1044,7 @@ W32KAPI BOOL    WINAPI NtUserTranslateMessage( const MSG *msg, UINT flags );
 W32KAPI BOOL    WINAPI NtUserUnhookWinEvent( HWINEVENTHOOK hEventHook );
 W32KAPI BOOL    WINAPI NtUserUnhookWindowsHook( INT id, HOOKPROC proc );
 W32KAPI BOOL    WINAPI NtUserUnhookWindowsHookEx( HHOOK handle );
-W32KAPI BOOL    WINAPI NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance,
-                                              struct client_menu_name *client_menu_name );
+W32KAPI BOOL    WINAPI NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance, struct client_menu_name **menu_name );
 W32KAPI BOOL    WINAPI NtUserUnregisterHotKey( HWND hwnd, INT id );
 W32KAPI BOOL    WINAPI NtUserUpdateInputContext( HIMC handle, UINT attr, UINT_PTR value );
 W32KAPI BOOL    WINAPI NtUserUpdateLayeredWindow( HWND hwnd, HDC hdc_dst, const POINT *pts_dst, const SIZE *size,
diff --git a/include/objidlbase.idl b/include/objidlbase.idl
index ec27c6da0b01..f5fe3c23bee6 100644
--- a/include/objidlbase.idl
+++ b/include/objidlbase.idl
@@ -23,6 +23,8 @@ import "unknwn.idl";
 cpp_quote("#ifndef _OBJIDLBASE_")
 cpp_quote("#define _OBJIDLBASE_")
 
+cpp_quote ("EXTERN_C const GUID IID_ICallbackWithNoReentrancyToApplicationSTA;")
+
 interface IStream;
 interface IEnumString;
 
diff --git a/include/processthreadsapi.h b/include/processthreadsapi.h
index c389b616f7b9..cefd7023be58 100644
--- a/include/processthreadsapi.h
+++ b/include/processthreadsapi.h
@@ -27,6 +27,78 @@
 extern "C" {
 #endif
 
+typedef struct _PROCESS_INFORMATION
+{
+    HANDLE hProcess;
+    HANDLE hThread;
+    DWORD  dwProcessId;
+    DWORD  dwThreadId;
+} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
+
+typedef struct _PROC_THREAD_ATTRIBUTE_LIST *PPROC_THREAD_ATTRIBUTE_LIST, *LPPROC_THREAD_ATTRIBUTE_LIST;
+
+typedef struct _STARTUPINFOA
+{
+    DWORD  cb;
+    LPSTR  lpReserved;
+    LPSTR  lpDesktop;
+    LPSTR  lpTitle;
+    DWORD  dwX;
+    DWORD  dwY;
+    DWORD  dwXSize;
+    DWORD  dwYSize;
+    DWORD  dwXCountChars;
+    DWORD  dwYCountChars;
+    DWORD  dwFillAttribute;
+    DWORD  dwFlags;
+    WORD   wShowWindow;
+    WORD   cbReserved2;
+    BYTE  *lpReserved2;
+    HANDLE hStdInput;
+    HANDLE hStdOutput;
+    HANDLE hStdError;
+} STARTUPINFOA, *LPSTARTUPINFOA;
+
+typedef struct _STARTUPINFOW
+{
+    DWORD  cb;
+    LPWSTR lpReserved;
+    LPWSTR lpDesktop;
+    LPWSTR lpTitle;
+    DWORD  dwX;
+    DWORD  dwY;
+    DWORD  dwXSize;
+    DWORD  dwYSize;
+    DWORD  dwXCountChars;
+    DWORD  dwYCountChars;
+    DWORD  dwFillAttribute;
+    DWORD  dwFlags;
+    WORD   wShowWindow;
+    WORD   cbReserved2;
+    BYTE  *lpReserved2;
+    HANDLE hStdInput;
+    HANDLE hStdOutput;
+    HANDLE hStdError;
+} STARTUPINFOW, *LPSTARTUPINFOW;
+
+DECL_WINELIB_TYPE_AW(STARTUPINFO)
+DECL_WINELIB_TYPE_AW(LPSTARTUPINFO)
+
+typedef enum _PROCESS_INFORMATION_CLASS
+{
+    ProcessMemoryPriority,
+    ProcessMemoryExhaustionInfo,
+    ProcessAppMemoryInfo,
+    ProcessInPrivateInfo,
+    ProcessPowerThrottling,
+    ProcessReservedValue1,
+    ProcessTelemetryCoverageInfo,
+    ProcessProtectionLevelInfo,
+    ProcessLeapSecondInfo,
+    ProcessMachineTypeInfo,
+    ProcessInformationClassMax
+} PROCESS_INFORMATION_CLASS;
+
 typedef enum _THREAD_INFORMATION_CLASS
 {
     ThreadMemoryPriority,
@@ -43,6 +115,13 @@ typedef enum _MACHINE_ATTRIBUTES
     Wow64Container = 0x00000004,
 } MACHINE_ATTRIBUTES;
 
+typedef struct _PROCESS_MACHINE_INFORMATION
+{
+    USHORT ProcessMachine;
+    USHORT Res0;
+    MACHINE_ATTRIBUTES MachineAttributes;
+} PROCESS_MACHINE_INFORMATION;
+
 typedef struct _MEMORY_PRIORITY_INFORMATION
 {
     ULONG MemoryPriority;
@@ -55,11 +134,7 @@ typedef struct _THREAD_POWER_THROTTLING_STATE
     ULONG StateMask;
 } THREAD_POWER_THROTTLING_STATE;
 
-WINBASEAPI VOID WINAPI GetCurrentThreadStackLimits(ULONG_PTR *, ULONG_PTR *);
-WINBASEAPI HRESULT WINAPI GetMachineTypeAttributes(USHORT,MACHINE_ATTRIBUTES *);
-WINBASEAPI HRESULT WINAPI GetThreadDescription(HANDLE,PWSTR *);
-WINBASEAPI HRESULT WINAPI SetThreadDescription(HANDLE,PCWSTR);
-WINBASEAPI BOOL WINAPI SetThreadInformation(HANDLE,THREAD_INFORMATION_CLASS,LPVOID,DWORD);
+typedef void (CALLBACK *PAPCFUNC)(ULONG_PTR);
 
 typedef enum _QUEUE_USER_APC_FLAGS
 {
@@ -76,6 +151,127 @@ typedef struct _APC_CALLBACK_DATA
     ULONG_PTR Reserved1;
 } APC_CALLBACK_DATA, *PAPC_CALLBACK_DATA;
 
+WINBASEAPI BOOL        WINAPI CreateProcessA(LPCSTR,LPSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCSTR,LPSTARTUPINFOA,LPPROCESS_INFORMATION);
+WINBASEAPI BOOL        WINAPI CreateProcessW(LPCWSTR,LPWSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
+#define                       CreateProcess WINELIB_NAME_AW(CreateProcess)
+WINADVAPI  BOOL        WINAPI CreateProcessAsUserA(HANDLE,LPCSTR,LPSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCSTR,LPSTARTUPINFOA,LPPROCESS_INFORMATION);
+WINADVAPI  BOOL        WINAPI CreateProcessAsUserW(HANDLE,LPCWSTR,LPWSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
+#define                       CreateProcessAsUser WINELIB_NAME_AW(CreateProcessAsUser)
+WINBASEAPI HANDLE      WINAPI CreateRemoteThread(HANDLE,LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPDWORD);
+WINBASEAPI HANDLE      WINAPI CreateRemoteThreadEx(HANDLE,LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPPROC_THREAD_ATTRIBUTE_LIST,LPDWORD);
+WINBASEAPI HANDLE      WINAPI CreateThread(LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPDWORD);
+WINBASEAPI void        WINAPI DeleteProcThreadAttributeList(struct _PROC_THREAD_ATTRIBUTE_LIST*);
+WINBASEAPI VOID DECLSPEC_NORETURN WINAPI ExitProcess(DWORD);
+WINBASEAPI VOID DECLSPEC_NORETURN WINAPI ExitThread(DWORD);
+WINBASEAPI BOOL        WINAPI FlushInstructionCache(HANDLE,LPCVOID,SIZE_T);
+WINBASEAPI VOID        WINAPI FlushProcessWriteBuffers(void);
+WINBASEAPI DWORD       WINAPI GetCurrentProcessorNumber(void);
+WINBASEAPI VOID        WINAPI GetCurrentProcessorNumberEx(PPROCESSOR_NUMBER);
+WINBASEAPI VOID        WINAPI GetCurrentThreadStackLimits(ULONG_PTR *, ULONG_PTR *);
+WINBASEAPI BOOL        WINAPI GetExitCodeProcess(HANDLE,LPDWORD);
+WINBASEAPI BOOL        WINAPI GetExitCodeThread(HANDLE,LPDWORD);
+WINBASEAPI HRESULT     WINAPI GetMachineTypeAttributes(USHORT,MACHINE_ATTRIBUTES *);
+WINBASEAPI DWORD       WINAPI GetPriorityClass(HANDLE);
+WINBASEAPI DWORD       WINAPI GetProcessId(HANDLE);
+WINBASEAPI DWORD       WINAPI GetProcessIdOfThread(HANDLE);
+WINBASEAPI BOOL        WINAPI GetProcessInformation(HANDLE,PROCESS_INFORMATION_CLASS,void*,DWORD);
+WINBASEAPI BOOL        WINAPI GetProcessPriorityBoost(HANDLE,PBOOL);
+WINBASEAPI BOOL        WINAPI GetProcessShutdownParameters(LPDWORD,LPDWORD);
+WINBASEAPI BOOL        WINAPI GetProcessTimes(HANDLE,LPFILETIME,LPFILETIME,LPFILETIME,LPFILETIME);
+WINBASEAPI DWORD       WINAPI GetProcessVersion(DWORD);
+WINBASEAPI VOID        WINAPI GetStartupInfoW(LPSTARTUPINFOW);
+WINBASEAPI BOOL        WINAPI GetThreadContext(HANDLE,CONTEXT *);
+WINBASEAPI HRESULT     WINAPI GetThreadDescription(HANDLE,PWSTR *);
+WINBASEAPI BOOL        WINAPI GetThreadIOPendingFlag(HANDLE,PBOOL);
+WINBASEAPI DWORD       WINAPI GetThreadId(HANDLE);
+WINBASEAPI INT         WINAPI GetThreadPriority(HANDLE);
+WINBASEAPI BOOL        WINAPI GetThreadPriorityBoost(HANDLE,PBOOL);
+WINBASEAPI BOOL        WINAPI GetThreadTimes(HANDLE,LPFILETIME,LPFILETIME,LPFILETIME,LPFILETIME);
+WINBASEAPI BOOL        WINAPI InitializeProcThreadAttributeList(struct _PROC_THREAD_ATTRIBUTE_LIST*,DWORD,DWORD,SIZE_T*);
+WINBASEAPI BOOL        WINAPI IsProcessorFeaturePresent(DWORD);
+WINBASEAPI HANDLE      WINAPI OpenProcess(DWORD,BOOL,DWORD);
+WINADVAPI  BOOL        WINAPI OpenProcessToken(HANDLE,DWORD,PHANDLE);
+WINBASEAPI HANDLE      WINAPI OpenThread(DWORD,BOOL,DWORD);
+WINADVAPI  BOOL        WINAPI OpenThreadToken(HANDLE,DWORD,BOOL,PHANDLE);
+WINBASEAPI BOOL        WINAPI ProcessIdToSessionId(DWORD,DWORD*);
+WINBASEAPI DWORD       WINAPI QueueUserAPC(PAPCFUNC,HANDLE,ULONG_PTR);
+WINBASEAPI DWORD       WINAPI QueueUserAPC2(PAPCFUNC,HANDLE,ULONG_PTR,QUEUE_USER_APC_FLAGS);
+WINBASEAPI DWORD       WINAPI ResumeThread(HANDLE);
+WINBASEAPI BOOL        WINAPI SetPriorityClass(HANDLE,DWORD);
+WINBASEAPI BOOL        WINAPI SetProcessInformation(HANDLE,PROCESS_INFORMATION_CLASS,LPVOID,DWORD);
+WINBASEAPI BOOL        WINAPI SetProcessPriorityBoost(HANDLE,BOOL);
+WINBASEAPI BOOL        WINAPI SetProcessShutdownParameters(DWORD,DWORD);
+WINBASEAPI BOOL        WINAPI SetThreadContext(HANDLE,const CONTEXT *);
+WINBASEAPI HRESULT     WINAPI SetThreadDescription(HANDLE,PCWSTR);
+WINBASEAPI DWORD       WINAPI SetThreadIdealProcessor(HANDLE,DWORD);
+WINBASEAPI BOOL        WINAPI SetThreadIdealProcessorEx(HANDLE, PROCESSOR_NUMBER *, PROCESSOR_NUMBER *);
+WINBASEAPI BOOL        WINAPI SetThreadInformation(HANDLE,THREAD_INFORMATION_CLASS,LPVOID,DWORD);
+WINBASEAPI BOOL        WINAPI SetThreadPriority(HANDLE,INT);
+WINBASEAPI BOOL        WINAPI SetThreadPriorityBoost(HANDLE,BOOL);
+WINADVAPI  BOOL        WINAPI SetThreadToken(PHANDLE,HANDLE);
+WINBASEAPI DWORD       WINAPI SuspendThread(HANDLE);
+WINBASEAPI BOOL        WINAPI SwitchToThread(void);
+WINBASEAPI BOOL        WINAPI TerminateProcess(HANDLE,UINT);
+WINBASEAPI BOOL        WINAPI TerminateThread(HANDLE,DWORD);
+WINBASEAPI DWORD       WINAPI TlsAlloc(void);
+WINBASEAPI BOOL        WINAPI TlsFree(DWORD);
+WINBASEAPI LPVOID      WINAPI TlsGetValue(DWORD);
+WINBASEAPI BOOL        WINAPI TlsSetValue(DWORD,LPVOID);
+WINBASEAPI BOOL        WINAPI UpdateProcThreadAttribute(struct _PROC_THREAD_ATTRIBUTE_LIST*,DWORD,DWORD_PTR,void*,SIZE_T,void*,SIZE_T*);
+
+#ifdef WINE_UNIX_LIB
+
+#define GetCurrentProcess()   NtCurrentProcess()
+#define GetCurrentThread()    NtCurrentThread()
+#define GetCurrentProcessId() HandleToULong(PsGetCurrentProcessId())
+#define GetCurrentThreadId()  HandleToULong(PsGetCurrentThreadId())
+
+#elif defined(__WINESRC__)
+
+static FORCEINLINE HANDLE WINAPI GetCurrentProcess(void)
+{
+    return (HANDLE)~(ULONG_PTR)0;
+}
+
+static FORCEINLINE DWORD WINAPI GetCurrentProcessId(void)
+{
+    return HandleToULong( ((HANDLE *)NtCurrentTeb())[8] );
+}
+
+static FORCEINLINE HANDLE WINAPI GetCurrentThread(void)
+{
+    return (HANDLE)~(ULONG_PTR)1;
+}
+
+static FORCEINLINE DWORD WINAPI GetCurrentThreadId(void)
+{
+    return HandleToULong( ((HANDLE *)NtCurrentTeb())[9] );
+}
+
+#else  /* __WINESRC__ */
+
+WINBASEAPI HANDLE      WINAPI GetCurrentProcess(void);
+WINBASEAPI DWORD       WINAPI GetCurrentProcessId(void);
+WINBASEAPI HANDLE      WINAPI GetCurrentThread(void);
+WINBASEAPI DWORD       WINAPI GetCurrentThreadId(void);
+
+#endif  /* __WINESRC__ */
+
+static FORCEINLINE HANDLE WINAPI GetCurrentProcessToken(void)
+{
+    return (HANDLE)~(ULONG_PTR)3;
+}
+
+static FORCEINLINE HANDLE WINAPI GetCurrentThreadToken(void)
+{
+    return (HANDLE)~(ULONG_PTR)4;
+}
+
+static FORCEINLINE HANDLE WINAPI GetCurrentThreadEffectiveToken(void)
+{
+    return (HANDLE)~(ULONG_PTR)5;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/rtscom.idl b/include/rtscom.idl
new file mode 100644
index 000000000000..e8a1b9e14855
--- /dev/null
+++ b/include/rtscom.idl
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2026 Alistair Leslie-Hughes
+ *
+ * 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
+ */
+
+import "oaidl.idl";
+import "msinkaut.idl";
+import "tpcshrd.idl";
+
+interface IRealTimeStylus;
+
+typedef DWORD CURSOR_ID;
+typedef CURSOR_ID STYLUS_ID;
+
+typedef enum StylusQueue
+{
+    SyncStylusQueue           = 0x1,
+    AsyncStylusQueueImmediate = 0x2,
+    AsyncStylusQueue          = 0x3
+} StylusQueue;
+
+typedef struct StylusInfo
+{
+    TABLET_CONTEXT_ID tcid;
+    STYLUS_ID cid;
+    BOOL bIsInvertedCursor;
+} StylusInfo;
+
+typedef
+enum RealTimeStylusDataInterest
+{
+    RTSDI_AllData                = 0xffffffff,
+    RTSDI_None                   = 0,
+    RTSDI_Error                  = 0x1,
+    RTSDI_RealTimeStylusEnabled  = 0x2,
+    RTSDI_RealTimeStylusDisabled = 0x4,
+    RTSDI_StylusNew	             = 0x8,
+    RTSDI_StylusInRange          = 0x10,
+    RTSDI_InAirPackets           = 0x20,
+    RTSDI_StylusOutOfRange       = 0x40,
+    RTSDI_StylusDown             = 0x80,
+    RTSDI_Packets                = 0x100,
+    RTSDI_StylusUp               = 0x200,
+    RTSDI_StylusButtonUp         = 0x400,
+    RTSDI_StylusButtonDown       = 0x800,
+    RTSDI_SystemEvents           = 0x1000,
+    RTSDI_TabletAdded            = 0x2000,
+    RTSDI_TabletRemoved          = 0x4000,
+    RTSDI_CustomStylusDataAdded  = 0x8000,
+    RTSDI_UpdateMapping          = 0x10000,
+    RTSDI_DefaultEvents          = RTSDI_RealTimeStylusEnabled | RTSDI_RealTimeStylusDisabled | RTSDI_StylusDown |
+                RTSDI_Packets | RTSDI_StylusUp | RTSDI_SystemEvents | RTSDI_CustomStylusDataAdded
+} RealTimeStylusDataInterest;
+
+[
+    object,
+    uuid (a81436d8-4757-4fd1-a185-133f97c6c545)
+]
+interface IStylusPlugin : IUnknown
+{
+   HRESULT RealTimeStylusEnabled( [in] IRealTimeStylus *piRtsSrc, [in, range(0,8)] ULONG cTcidCount,
+        [in, size_is(cTcidCount)] const TABLET_CONTEXT_ID *pTcids);
+    HRESULT RealTimeStylusDisabled( [in] IRealTimeStylus *piRtsSrc, [in, range(0,8)] ULONG cTcidCount,
+        [in, size_is(cTcidCount)] const TABLET_CONTEXT_ID *pTcids);
+    HRESULT StylusInRange( [in] IRealTimeStylus *piRtsSrc, [in] TABLET_CONTEXT_ID tcid, [in] STYLUS_ID sid);
+    HRESULT StylusOutOfRange( [in] IRealTimeStylus *piRtsSrc, [in] TABLET_CONTEXT_ID tcid, [in] STYLUS_ID sid);
+    HRESULT StylusDown( [in] IRealTimeStylus *piRtsSrc, [in] const StylusInfo *pStylusInfo,
+        [in, range(0,32)] ULONG cPropCountPerPkt, [in, size_is(cPropCountPerPkt)] LONG *pPacket, [out, in] LONG **ppInOutPkt);
+    HRESULT StylusUp( [in] IRealTimeStylus *piRtsSrc, [in] const StylusInfo *pStylusInfo,
+        [in, range(0,32)] ULONG cPropCountPerPkt, [in, size_is(cPropCountPerPkt)] LONG *pPacket, [out, in] LONG **ppInOutPkt);
+    HRESULT StylusButtonDown( [in] IRealTimeStylus *piRtsSrc, [in] STYLUS_ID sid, [in] const GUID *pGuidStylusButton,
+        [out, in] POINT *pStylusPos);
+    HRESULT StylusButtonUp( [in] IRealTimeStylus *piRtsSrc, [in] STYLUS_ID sid, [in] const GUID *pGuidStylusButton,
+        [out, in] POINT *pStylusPos);
+    HRESULT InAirPackets( [in] IRealTimeStylus *piRtsSrc, [in] const StylusInfo *pStylusInfo, [in] ULONG cPktCount,
+        [in, range(0,0x7fff)] ULONG cPktBuffLength, [in, size_is(cPktBuffLength)] LONG *pPackets,
+        [out, in] ULONG *pcInOutPkts, [out, in] LONG **ppInOutPkts);
+    HRESULT Packets( [in] IRealTimeStylus *piRtsSrc, [in] const StylusInfo *pStylusInfo, [in] ULONG cPktCount,
+        [in, range(0,0x7fff)] ULONG cPktBuffLength, [in, size_is(cPktBuffLength)] LONG *pPackets,
+        [out, in] ULONG *pcInOutPkts, [out, in] LONG **ppInOutPkts);
+    HRESULT CustomStylusDataAdded( [in] IRealTimeStylus *piRtsSrc, [in] const GUID *pGuidId,
+        [in, range(0,0x7fff)] ULONG cbData, [unique, in, size_is(cbData)] const BYTE *pbData);
+    HRESULT SystemEvent( [in] IRealTimeStylus *piRtsSrc, [in] TABLET_CONTEXT_ID tcid, [in] STYLUS_ID sid,
+        [in] SYSTEM_EVENT event, [in] SYSTEM_EVENT_DATA eventdata);
+    HRESULT TabletAdded( [in] IRealTimeStylus *piRtsSrc, [in] IInkTablet *piTablet);
+    HRESULT TabletRemoved( [in] IRealTimeStylus *piRtsSrc, [in] LONG iTabletIndex);
+    HRESULT Error( [in] IRealTimeStylus *piRtsSrc, [in] IStylusPlugin *piPlugin, [in] RealTimeStylusDataInterest dataInterest,
+        [in] HRESULT hrErrorCode, [out, in] LONG_PTR *lptrKey);
+    HRESULT UpdateMapping( [in] IRealTimeStylus *piRtsSrc);
+    HRESULT DataInterest(  [retval, out] RealTimeStylusDataInterest *pDataInterest);
+}
+
+[
+    object,
+    uuid (a157b174-482f-4d71-a3f6-3a41ddd11be9)
+]
+interface IStylusSyncPlugin : IStylusPlugin
+{
+    /* Has no functions */
+}
+
+[
+    object,
+    uuid (a7cca85a-31bc-4cd2-aadc-3289a3af11c8)
+]
+interface IStylusAsyncPlugin : IStylusPlugin
+{
+    /* Has no functions */
+}
+
+[
+    object,
+    uuid (a8bb5d22-3144-4a7b-93cd-f34a16be513a)
+]
+interface IRealTimeStylus : IUnknown
+{
+    [propget]
+    HRESULT Enabled( [retval, out] BOOL *pfEnable );
+
+    [propput]
+    HRESULT Enabled( [in] BOOL fEnable );
+
+    [propget]
+    HRESULT HWND( [retval, out]  HANDLE_PTR *phwnd );
+
+    [propput]
+    HRESULT HWND( [in] HANDLE_PTR hwnd );
+
+    [propget]
+    HRESULT WindowInputRectangle( [retval, out] RECT *prcWndInputRect );
+
+    [propput]
+    HRESULT WindowInputRectangle([in] const RECT *prcWndInputRect );
+
+    HRESULT AddStylusSyncPlugin( [in] ULONG iIndex, [in] IStylusSyncPlugin *piPlugin );
+    HRESULT RemoveStylusSyncPlugin( [in] ULONG iIndex, [out, in] IStylusSyncPlugin **ppiPlugin );
+    HRESULT RemoveAllStylusSyncPlugins( void );
+    HRESULT GetStylusSyncPlugin([in] ULONG iIndex, [out] IStylusSyncPlugin **ppiPlugin );
+    HRESULT GetStylusSyncPluginCount( [out] ULONG *pcPlugins );
+    HRESULT AddStylusAsyncPlugin( [in] ULONG iIndex, [in] IStylusAsyncPlugin *piPlugin );
+    HRESULT RemoveStylusAsyncPlugin( [in] ULONG iIndex, [out, in] IStylusAsyncPlugin **ppiPlugin );
+    HRESULT RemoveAllStylusAsyncPlugins( void );
+    HRESULT GetStylusAsyncPlugin( [in] ULONG iIndex, [out] IStylusAsyncPlugin **ppiPlugin);
+    HRESULT GetStylusAsyncPluginCount( [out] ULONG *pcPlugins );
+
+    [propget]
+    HRESULT ChildRealTimeStylusPlugin( [retval, out] IRealTimeStylus **ppiRTS );
+
+    [propputref]
+    HRESULT ChildRealTimeStylusPlugin( [unique, in] IRealTimeStylus *piRTS);
+
+    HRESULT AddCustomStylusDataToQueue( [in] StylusQueue sq, [in] const GUID *pGuidId,
+                [range(0,0x7fff), in] ULONG cbData,
+                [unique, size_is(cbData), in] BYTE *pbData );
+    HRESULT ClearStylusQueues( void );
+    HRESULT SetAllTabletsMode( [in] BOOL fUseMouseForInput );
+    HRESULT SetSingleTabletMode( [in] IInkTablet *piTablet);
+    HRESULT GetTablet( [retval, out] IInkTablet **ppiSingleTablet );
+    HRESULT GetTabletContextIdFromTablet( [in] IInkTablet *piTablet, [retval, out]  TABLET_CONTEXT_ID *ptcid);
+    HRESULT GetTabletFromTabletContextId([in] TABLET_CONTEXT_ID tcid, [retval, out] IInkTablet **ppiTablet);
+
+    HRESULT GetAllTabletContextIds( [out, in] ULONG *pcTcidCount, [size_is(*pcTcidCount), out] TABLET_CONTEXT_ID **ppTcids);
+
+    HRESULT GetStyluses( [retval, out] IInkCursors **ppiInkCursors );
+    HRESULT GetStylusForId( [in] STYLUS_ID sid, [retval, out] IInkCursor **ppiInkCursor );
+    HRESULT SetDesiredPacketDescription( [in, range(0,32)] ULONG cProperties, [in, size_is(cProperties)] const GUID *pPropertyGuids);
+
+    HRESULT GetDesiredPacketDescription( [out, in] ULONG *pcProperties,
+                [size_is(*pcProperties), out] GUID **ppPropertyGuids);
+
+    HRESULT GetPacketDescriptionData( [in] TABLET_CONTEXT_ID tcid, [unique, out, in] FLOAT *pfInkToDeviceScaleX,
+                [unique, out, in] FLOAT *pfInkToDeviceScaleY, [out, in] ULONG *pcPacketProperties,
+                [size_is(*pcPacketProperties), out] PACKET_PROPERTY **ppPacketProperties);
+};
+
+[
+    uuid(e26b366d-f998-43ce-836f-cb6d904432b0),
+    threading(both)
+]
+coclass RealTimeStylus
+{
+    [default] interface IRealTimeStylus;
+}
diff --git a/include/tpcshrd.idl b/include/tpcshrd.idl
new file mode 100644
index 000000000000..31c10785e4bc
--- /dev/null
+++ b/include/tpcshrd.idl
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 Alistair Leslie-Hughes
+ *
+ * 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
+ */
+
+import "unknwn.idl";
+import "objidl.idl";
+import "oaidl.idl";
+import "msinkaut.idl";
+
+cpp_quote("#define TABLET_DISABLE_PRESSANDHOLD        0x00000001")
+
+cpp_quote("#define WM_TABLET_DEFBASE                  0x02c0")
+cpp_quote("#define WM_TABLET_MAXOFFSET                0x20")
+cpp_quote("#define WM_TABLET_ADDED                    (WM_TABLET_DEFBASE + 8)" )
+cpp_quote("#define WM_TABLET_DELETED                  (WM_TABLET_DEFBASE + 9)" )
+cpp_quote("#define WM_TABLET_FLICK                    (WM_TABLET_DEFBASE + 11)" )
+cpp_quote("#define WM_TABLET_QUERYSYSTEMGESTURESTATUS (WM_TABLET_DEFBASE + 12)" )
+
+typedef USHORT SYSTEM_EVENT;
+typedef DWORD TABLET_CONTEXT_ID;
+
+typedef enum _PROPERTY_UNITS
+{
+    PROPERTY_UNITS_DEFAULT     = 0,
+    PROPERTY_UNITS_INCHES      = 1,
+    PROPERTY_UNITS_CENTIMETERS = 2,
+    PROPERTY_UNITS_DEGREES     = 3,
+    PROPERTY_UNITS_RADIANS     = 4,
+    PROPERTY_UNITS_SECONDS     = 5,
+    PROPERTY_UNITS_POUNDS      = 6,
+    PROPERTY_UNITS_GRAMS       = 7,
+    PROPERTY_UNITS_SILINEAR    = 8,
+    PROPERTY_UNITS_SIROTATION  = 9,
+    PROPERTY_UNITS_ENGLINEAR   = 10,
+    PROPERTY_UNITS_ENGROTATION = 11,
+    PROPERTY_UNITS_SLUGS       = 12,
+    PROPERTY_UNITS_KELVIN      = 13,
+    PROPERTY_UNITS_FAHRENHEIT  = 14,
+    PROPERTY_UNITS_AMPERE      = 15,
+    PROPERTY_UNITS_CANDELA     = 16
+} PROPERTY_UNITS;
+
+typedef struct _PROPERTY_METRICS
+{
+    LONG nLogicalMin;
+    LONG nLogicalMax;
+    PROPERTY_UNITS Units;
+    FLOAT fResolution;
+} PROPERTY_METRICS;
+
+typedef struct _PROPERTY_METRICS *PPROPERTY_METRICS;
+
+typedef struct _PACKET_PROPERTY
+{
+    GUID guid;
+    PROPERTY_METRICS PropertyMetrics;
+} PACKET_PROPERTY;
+
+typedef struct _PACKET_PROPERTY *PPACKET_PROPERTY;
+
+typedef struct tagSYSTEM_EVENT_DATA
+{
+    BYTE bModifier;
+    WCHAR wKey;
+    LONG xPos;
+    LONG yPos;
+    BYTE bCursorMode;
+    DWORD dwButtonState;
+} SYSTEM_EVENT_DATA;
diff --git a/include/uiautomationcoreapi.h b/include/uiautomationcoreapi.h
index fbfbc46b09c0..3316aff8a545 100644
--- a/include/uiautomationcoreapi.h
+++ b/include/uiautomationcoreapi.h
@@ -560,6 +560,7 @@ HRESULT WINAPI UiaEventAddWindow(HUIAEVENT huiaevent, HWND hwnd);
 HRESULT WINAPI UiaEventRemoveWindow(HUIAEVENT huiaevent, HWND hwnd);
 BOOL WINAPI UiaHasServerSideProvider(HWND hwnd);
 BOOL WINAPI UiaClientsAreListening(void);
+HRESULT WINAPI UiaDisconnectAllProviders(void);
 
 #ifdef __cplusplus
 }
diff --git a/include/winbase.h b/include/winbase.h
index eab6986653fc..db7a63f90e1f 100644
--- a/include/winbase.h
+++ b/include/winbase.h
@@ -340,58 +340,6 @@ typedef VOID (CALLBACK *LPOVERLAPPED_COMPLETION_ROUTINE)(DWORD,DWORD,LPOVERLAPPE
 #define STARTF_UNTRUSTEDSOURCE  0x00008000
 #define STARTF_HOLOGRAPHIC      0x00040000
 
-typedef struct _STARTUPINFOA{
-        DWORD cb;		/* 00: size of struct */
-        LPSTR lpReserved;	/* 04: */
-        LPSTR lpDesktop;	/* 08: */
-        LPSTR lpTitle;		/* 0c: */
-        DWORD dwX;		/* 10: */
-        DWORD dwY;		/* 14: */
-        DWORD dwXSize;		/* 18: */
-        DWORD dwYSize;		/* 1c: */
-        DWORD dwXCountChars;	/* 20: */
-        DWORD dwYCountChars;	/* 24: */
-        DWORD dwFillAttribute;	/* 28: */
-        DWORD dwFlags;		/* 2c: */
-        WORD wShowWindow;	/* 30: */
-        WORD cbReserved2;	/* 32: */
-        BYTE *lpReserved2;	/* 34: */
-        HANDLE hStdInput;	/* 38: */
-        HANDLE hStdOutput;	/* 3c: */
-        HANDLE hStdError;	/* 40: */
-} STARTUPINFOA, *LPSTARTUPINFOA;
-
-typedef struct _STARTUPINFOW{
-        DWORD cb;
-        LPWSTR lpReserved;
-        LPWSTR lpDesktop;
-        LPWSTR lpTitle;
-        DWORD dwX;
-        DWORD dwY;
-        DWORD dwXSize;
-        DWORD dwYSize;
-        DWORD dwXCountChars;
-        DWORD dwYCountChars;
-        DWORD dwFillAttribute;
-        DWORD dwFlags;
-        WORD wShowWindow;
-        WORD cbReserved2;
-        BYTE *lpReserved2;
-        HANDLE hStdInput;
-        HANDLE hStdOutput;
-        HANDLE hStdError;
-} STARTUPINFOW, *LPSTARTUPINFOW;
-
-DECL_WINELIB_TYPE_AW(STARTUPINFO)
-DECL_WINELIB_TYPE_AW(LPSTARTUPINFO)
-
-typedef struct _PROCESS_INFORMATION{
-	HANDLE	hProcess;
-	HANDLE	hThread;
-	DWORD		dwProcessId;
-	DWORD		dwThreadId;
-} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
-
 /* CreateProcess: dwCreationFlag values
  */
 #define DEBUG_PROCESS                     0x00000001
@@ -1201,7 +1149,6 @@ typedef struct tagCOMMTIMEOUTS {
 
 #define PROCESS_NAME_NATIVE        1
 
-typedef void (CALLBACK *PAPCFUNC)(ULONG_PTR);
 typedef void (CALLBACK *PTIMERAPCROUTINE)(LPVOID,DWORD,DWORD);
 
 #define HW_PROFILE_GUIDLEN	39
@@ -1316,9 +1263,6 @@ typedef BOOL (WINAPI *PINIT_ONCE_FN)(PINIT_ONCE,PVOID,PVOID*);
 #define RESOURCE_ENUM_MUI_SYSTEM  0x0004
 #define RESOURCE_ENUM_VALIDATE    0x0008
 
-typedef struct _PROC_THREAD_ATTRIBUTE_LIST
-*PPROC_THREAD_ATTRIBUTE_LIST, *LPPROC_THREAD_ATTRIBUTE_LIST;
-
 #define PROC_THREAD_ATTRIBUTE_NUMBER   0x0000ffff
 #define PROC_THREAD_ATTRIBUTE_THREAD   0x00010000
 #define PROC_THREAD_ATTRIBUTE_INPUT    0x00020000
@@ -1421,28 +1365,6 @@ typedef struct _WIN32_MEMORY_RANGE_ENTRY
     SIZE_T NumberOfBytes;
 } WIN32_MEMORY_RANGE_ENTRY, *PWIN32_MEMORY_RANGE_ENTRY;
 
-typedef struct _PROCESS_MACHINE_INFORMATION
-{
-    USHORT ProcessMachine;
-    USHORT Res0;
-    MACHINE_ATTRIBUTES MachineAttributes;
-} PROCESS_MACHINE_INFORMATION;
-
-typedef enum _PROCESS_INFORMATION_CLASS
-{
-    ProcessMemoryPriority,
-    ProcessMemoryExhaustionInfo,
-    ProcessAppMemoryInfo,
-    ProcessInPrivateInfo,
-    ProcessPowerThrottling,
-    ProcessReservedValue1,
-    ProcessTelemetryCoverageInfo,
-    ProcessProtectionLevelInfo,
-    ProcessLeapSecondInfo,
-    ProcessMachineTypeInfo,
-    ProcessInformationClassMax
-} PROCESS_INFORMATION_CLASS;
-
 typedef struct DISK_SPACE_INFORMATION
 {
     ULONGLONG ActualTotalAllocationUnits;
@@ -1603,19 +1525,11 @@ WINBASEAPI BOOL        WINAPI CreatePipe(PHANDLE,PHANDLE,LPSECURITY_ATTRIBUTES,D
 WINADVAPI  BOOL        WINAPI CreatePrivateObjectSecurity(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR*,BOOL,HANDLE,PGENERIC_MAPPING);
 WINADVAPI  BOOL        WINAPI CreatePrivateObjectSecurityEx(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR*,GUID*,BOOL,ULONG,HANDLE,PGENERIC_MAPPING);
 WINADVAPI  BOOL        WINAPI CreatePrivateObjectSecurityWithMultipleInheritance(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR*,GUID**,ULONG,BOOL,ULONG,HANDLE,PGENERIC_MAPPING);
-WINBASEAPI BOOL        WINAPI CreateProcessA(LPCSTR,LPSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCSTR,LPSTARTUPINFOA,LPPROCESS_INFORMATION);
-WINBASEAPI BOOL        WINAPI CreateProcessW(LPCWSTR,LPWSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
-#define                       CreateProcess WINELIB_NAME_AW(CreateProcess)
-WINADVAPI  BOOL        WINAPI CreateProcessAsUserA(HANDLE,LPCSTR,LPSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCSTR,LPSTARTUPINFOA,LPPROCESS_INFORMATION);
-WINADVAPI  BOOL        WINAPI CreateProcessAsUserW(HANDLE,LPCWSTR,LPWSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
-#define                       CreateProcessAsUser WINELIB_NAME_AW(CreateProcessAsUser)
 WINBASEAPI BOOL        WINAPI CreateProcessInternalA(HANDLE,LPCSTR,LPSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCSTR,LPSTARTUPINFOA,LPPROCESS_INFORMATION,HANDLE*);
 WINBASEAPI BOOL        WINAPI CreateProcessInternalW(HANDLE,LPCWSTR,LPWSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION,HANDLE*);
 #define                       CreateProcessInternal WINELIB_NAME_AW(CreateProcessInternal)
 WINADVAPI  BOOL        WINAPI CreateProcessWithLogonW(LPCWSTR,LPCWSTR,LPCWSTR,DWORD,LPCWSTR,LPWSTR,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
 WINADVAPI  BOOL        WINAPI CreateProcessWithTokenW(HANDLE,DWORD,LPCWSTR,LPWSTR,DWORD,void *,LPCWSTR,STARTUPINFOW *,PROCESS_INFORMATION *);
-WINBASEAPI HANDLE      WINAPI CreateRemoteThread(HANDLE,LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPDWORD);
-WINBASEAPI HANDLE      WINAPI CreateRemoteThreadEx(HANDLE,LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPPROC_THREAD_ATTRIBUTE_LIST,LPDWORD);
 WINADVAPI  BOOL        WINAPI CreateRestrictedToken(HANDLE,DWORD,DWORD,PSID_AND_ATTRIBUTES,DWORD,PLUID_AND_ATTRIBUTES,DWORD,PSID_AND_ATTRIBUTES,PHANDLE);
 WINBASEAPI HANDLE      WINAPI CreateSemaphoreA(LPSECURITY_ATTRIBUTES,LONG,LONG,LPCSTR);
 WINBASEAPI HANDLE      WINAPI CreateSemaphoreW(LPSECURITY_ATTRIBUTES,LONG,LONG,LPCWSTR);
@@ -1630,7 +1544,6 @@ WINBASEAPI BOOL        WINAPI CreateHardLinkA(LPCSTR,LPCSTR,LPSECURITY_ATTRIBUTE
 WINBASEAPI BOOL        WINAPI CreateHardLinkW(LPCWSTR,LPCWSTR,LPSECURITY_ATTRIBUTES);
 #define                       CreateHardLink WINELIB_NAME_AW(CreateHardLink)
 WINBASEAPI DWORD       WINAPI CreateTapePartition(HANDLE,DWORD,DWORD,DWORD);
-WINBASEAPI HANDLE      WINAPI CreateThread(LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPDWORD);
 WINBASEAPI HANDLE      WINAPI CreateTimerQueue(void);
 WINBASEAPI BOOL        WINAPI CreateTimerQueueTimer(PHANDLE,HANDLE,WAITORTIMERCALLBACK,PVOID,DWORD,DWORD,ULONG);
 WINBASEAPI BOOL        WINAPI CreateUmsCompletionList(PUMS_COMPLETION_LIST*);
@@ -1669,7 +1582,6 @@ static inline BOOL DeleteFile( LPCTSTR file_name )
     return WINELIB_NAME_AW(DeleteFile)( file_name );
 }
 #endif
-WINBASEAPI void        WINAPI DeleteProcThreadAttributeList(struct _PROC_THREAD_ATTRIBUTE_LIST*);
 WINBASEAPI BOOL        WINAPI DeleteTimerQueue(HANDLE);
 WINBASEAPI BOOL        WINAPI DeleteTimerQueueEx(HANDLE,HANDLE);
 WINBASEAPI BOOL        WINAPI DeleteTimerQueueTimer(HANDLE,HANDLE,HANDLE);
@@ -1725,8 +1637,6 @@ WINADVAPI  BOOL        WINAPI EqualDomainSid(PSID,PSID,BOOL*);
 WINADVAPI  BOOL        WINAPI EqualSid(PSID, PSID);
 WINADVAPI  BOOL        WINAPI EqualPrefixSid(PSID,PSID);
 WINBASEAPI DWORD       WINAPI EraseTape(HANDLE,DWORD,BOOL);
-WINBASEAPI VOID DECLSPEC_NORETURN WINAPI ExitProcess(DWORD);
-WINBASEAPI VOID DECLSPEC_NORETURN WINAPI ExitThread(DWORD);
 WINBASEAPI BOOL        WINAPI ExecuteUmsThread(PUMS_CONTEXT);
 WINBASEAPI DWORD       WINAPI ExpandEnvironmentStringsA(LPCSTR,LPSTR,DWORD);
 WINBASEAPI DWORD       WINAPI ExpandEnvironmentStringsW(LPCWSTR,LPWSTR,DWORD);
@@ -1787,8 +1697,6 @@ WINBASEAPI BOOL        WINAPI FindNextVolumeMountPointW(HANDLE,LPWSTR,DWORD);
 WINBASEAPI BOOL        WINAPI FindVolumeClose(HANDLE);
 WINBASEAPI BOOL        WINAPI FindVolumeMountPointClose(HANDLE);
 WINBASEAPI BOOL        WINAPI FlushFileBuffers(HANDLE);
-WINBASEAPI BOOL        WINAPI FlushInstructionCache(HANDLE,LPCVOID,SIZE_T);
-WINBASEAPI VOID        WINAPI FlushProcessWriteBuffers(void);
 WINBASEAPI BOOL        WINAPI FlushViewOfFile(LPCVOID,SIZE_T);
 #ifdef __ms_va_list
 WINBASEAPI DWORD       WINAPI FormatMessageA(DWORD,LPCVOID,DWORD,DWORD,LPSTR,DWORD,__ms_va_list*);
@@ -1837,8 +1745,6 @@ WINBASEAPI UINT        WINAPI GetCurrentDirectoryW(UINT,LPWSTR);
 WINADVAPI  BOOL        WINAPI GetCurrentHwProfileA(LPHW_PROFILE_INFOA);
 WINADVAPI  BOOL        WINAPI GetCurrentHwProfileW(LPHW_PROFILE_INFOW);
 #define                       GetCurrentHwProfile WINELIB_NAME_AW(GetCurrentHwProfile)
-WINBASEAPI DWORD       WINAPI GetCurrentProcessorNumber(void);
-WINBASEAPI VOID        WINAPI GetCurrentProcessorNumberEx(PPROCESSOR_NUMBER);
 #define                       GetCurrentTime() GetTickCount()
 WINBASEAPI PUMS_CONTEXT WINAPI GetCurrentUmsThread(void);
 WINBASEAPI BOOL        WINAPI GetDefaultCommConfigA(LPCSTR,LPCOMMCONFIG,LPDWORD);
@@ -1869,8 +1775,6 @@ WINBASEAPI DWORD       WINAPI GetEnvironmentVariableW(LPCWSTR,LPWSTR,DWORD);
 #define                       GetEnvironmentVariable WINELIB_NAME_AW(GetEnvironmentVariable)
 WINBASEAPI UINT        WINAPI GetErrorMode(void);
 WINADVAPI  BOOL        WINAPI GetEventLogInformation(HANDLE,DWORD,LPVOID,DWORD,LPDWORD);
-WINBASEAPI BOOL        WINAPI GetExitCodeProcess(HANDLE,LPDWORD);
-WINBASEAPI BOOL        WINAPI GetExitCodeThread(HANDLE,LPDWORD);
 WINBASEAPI DWORD       WINAPI GetFileAttributesA(LPCSTR);
 WINBASEAPI DWORD       WINAPI GetFileAttributesW(LPCWSTR);
 #define                       GetFileAttributes WINELIB_NAME_AW(GetFileAttributes)
@@ -1936,7 +1840,6 @@ WINADVAPI  BOOL        WINAPI GetNumberOfEventLogRecords(HANDLE,PDWORD);
 WINADVAPI  BOOL        WINAPI GetOldestEventLogRecord(HANDLE,PDWORD);
 WINBASEAPI BOOL        WINAPI GetOverlappedResult(HANDLE,LPOVERLAPPED,LPDWORD,BOOL);
 WINBASEAPI BOOL        WINAPI GetOverlappedResultEx(HANDLE,OVERLAPPED*,DWORD*,DWORD,BOOL);
-WINBASEAPI DWORD       WINAPI GetPriorityClass(HANDLE);
 WINADVAPI  BOOL        WINAPI GetPrivateObjectSecurity(PSECURITY_DESCRIPTOR,SECURITY_INFORMATION,PSECURITY_DESCRIPTOR,DWORD,PDWORD);
 WINBASEAPI UINT        WINAPI GetPrivateProfileIntA(LPCSTR,LPCSTR,INT,LPCSTR);
 WINBASEAPI UINT        WINAPI GetPrivateProfileIntW(LPCWSTR,LPCWSTR,INT,LPCWSTR);
@@ -1957,14 +1860,7 @@ WINBASEAPI FARPROC     WINAPI GetProcAddress(HMODULE,LPCSTR);
 WINBASEAPI BOOL        WINAPI GetProcessAffinityMask(HANDLE,PDWORD_PTR,PDWORD_PTR);
 WINBASEAPI BOOL        WINAPI GetProcessGroupAffinity(HANDLE, PUSHORT, PUSHORT);
 WINBASEAPI DWORD       WINAPI GetProcessHeaps(DWORD,PHANDLE);
-WINBASEAPI DWORD       WINAPI GetProcessId(HANDLE);
-WINBASEAPI DWORD       WINAPI GetProcessIdOfThread(HANDLE);
-WINBASEAPI BOOL        WINAPI GetProcessInformation(HANDLE,PROCESS_INFORMATION_CLASS,void*,DWORD);
 WINBASEAPI BOOL        WINAPI GetProcessIoCounters(HANDLE,PIO_COUNTERS);
-WINBASEAPI BOOL        WINAPI GetProcessPriorityBoost(HANDLE,PBOOL);
-WINBASEAPI BOOL        WINAPI GetProcessShutdownParameters(LPDWORD,LPDWORD);
-WINBASEAPI BOOL        WINAPI GetProcessTimes(HANDLE,LPFILETIME,LPFILETIME,LPFILETIME,LPFILETIME);
-WINBASEAPI DWORD       WINAPI GetProcessVersion(DWORD);
 WINBASEAPI BOOL        WINAPI GetProcessWorkingSetSize(HANDLE,PSIZE_T,PSIZE_T);
 WINBASEAPI BOOL        WINAPI GetProcessWorkingSetSizeEx(HANDLE,SIZE_T*,SIZE_T*,DWORD*);
 WINBASEAPI UINT        WINAPI GetProfileIntA(LPCSTR,LPCSTR,INT);
@@ -1992,7 +1888,6 @@ WINBASEAPI DWORD       WINAPI GetShortPathNameA(LPCSTR,LPSTR,DWORD);
 WINBASEAPI DWORD       WINAPI GetShortPathNameW(LPCWSTR,LPWSTR,DWORD);
 #define                       GetShortPathName WINELIB_NAME_AW(GetShortPathName)
 WINBASEAPI VOID        WINAPI GetStartupInfoA(LPSTARTUPINFOA);
-WINBASEAPI VOID        WINAPI GetStartupInfoW(LPSTARTUPINFOW);
 #define                       GetStartupInfo WINELIB_NAME_AW(GetStartupInfo)
 WINBASEAPI HANDLE      WINAPI GetStdHandle(DWORD);
 WINBASEAPI BOOL        WINAPI GetSystemCpuSetInformation(SYSTEM_CPU_SET_INFORMATION*,ULONG,ULONG*,HANDLE,ULONG);
@@ -2016,15 +1911,9 @@ WINBASEAPI UINT        WINAPI GetTempFileNameW(LPCWSTR,LPCWSTR,UINT,LPWSTR);
 WINBASEAPI DWORD       WINAPI GetTempPathA(DWORD,LPSTR);
 WINBASEAPI DWORD       WINAPI GetTempPathW(DWORD,LPWSTR);
 #define                       GetTempPath WINELIB_NAME_AW(GetTempPath)
-WINBASEAPI BOOL        WINAPI GetThreadContext(HANDLE,CONTEXT *);
 WINBASEAPI DWORD       WINAPI GetThreadErrorMode(void);
 WINBASEAPI BOOL        WINAPI GetThreadGroupAffinity(HANDLE, PGROUP_AFFINITY);
-WINBASEAPI DWORD       WINAPI GetThreadId(HANDLE);
-WINBASEAPI BOOL        WINAPI GetThreadIOPendingFlag(HANDLE,PBOOL);
-WINBASEAPI INT         WINAPI GetThreadPriority(HANDLE);
-WINBASEAPI BOOL        WINAPI GetThreadPriorityBoost(HANDLE,PBOOL);
 WINBASEAPI BOOL        WINAPI GetThreadSelectorEntry(HANDLE,DWORD,LPLDT_ENTRY);
-WINBASEAPI BOOL        WINAPI GetThreadTimes(HANDLE,LPFILETIME,LPFILETIME,LPFILETIME,LPFILETIME);
 WINADVAPI  BOOL        WINAPI GetTokenInformation(HANDLE,TOKEN_INFORMATION_CLASS,LPVOID,DWORD,LPDWORD);
 WINBASEAPI BOOL        WINAPI GetUmsCompletionListEvent(PUMS_COMPLETION_LIST, PHANDLE);
 WINADVAPI  BOOL        WINAPI GetUserNameA(LPSTR,LPDWORD);
@@ -2093,7 +1982,6 @@ WINBASEAPI BOOL        WINAPI InitializeContext2(void *,DWORD,CONTEXT **,DWORD *
 WINBASEAPI void        WINAPI InitializeCriticalSection(CRITICAL_SECTION *lpCrit);
 WINBASEAPI BOOL        WINAPI InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION *,DWORD);
 WINBASEAPI BOOL        WINAPI InitializeCriticalSectionEx(CRITICAL_SECTION *,DWORD,DWORD);
-WINBASEAPI BOOL        WINAPI InitializeProcThreadAttributeList(struct _PROC_THREAD_ATTRIBUTE_LIST*,DWORD,DWORD,SIZE_T*);
 WINADVAPI  BOOL        WINAPI InitializeSecurityDescriptor(PSECURITY_DESCRIPTOR,DWORD);
 WINADVAPI  BOOL        WINAPI InitializeSid(PSID,PSID_IDENTIFIER_AUTHORITY,BYTE);
 WINBASEAPI VOID        WINAPI InitializeSListHead(PSLIST_HEADER);
@@ -2128,7 +2016,6 @@ WINADVAPI  BOOL        WINAPI ImpersonateLoggedOnUser(HANDLE);
 WINADVAPI  BOOL        WINAPI ImpersonateNamedPipeClient(HANDLE);
 WINADVAPI  BOOL        WINAPI ImpersonateSelf(SECURITY_IMPERSONATION_LEVEL);
 WINBASEAPI BOOL        WINAPI IsProcessInJob(HANDLE,HANDLE,PBOOL);
-WINBASEAPI BOOL        WINAPI IsProcessorFeaturePresent(DWORD);
 WINBASEAPI void        WINAPI LeaveCriticalSection(CRITICAL_SECTION *lpCrit);
 WINBASEAPI HMODULE     WINAPI LoadLibraryA(LPCSTR);
 WINBASEAPI HMODULE     WINAPI LoadLibraryW(LPCWSTR);
@@ -2243,13 +2130,9 @@ WINBASEAPI HANDLE      WINAPI OpenJobObjectW(DWORD,BOOL,LPCWSTR);
 WINBASEAPI HANDLE      WINAPI OpenMutexA(DWORD,BOOL,LPCSTR);
 WINBASEAPI HANDLE      WINAPI OpenMutexW(DWORD,BOOL,LPCWSTR);
 #define                       OpenMutex WINELIB_NAME_AW(OpenMutex)
-WINBASEAPI HANDLE      WINAPI OpenProcess(DWORD,BOOL,DWORD);
-WINADVAPI  BOOL        WINAPI OpenProcessToken(HANDLE,DWORD,PHANDLE);
 WINBASEAPI HANDLE      WINAPI OpenSemaphoreA(DWORD,BOOL,LPCSTR);
 WINBASEAPI HANDLE      WINAPI OpenSemaphoreW(DWORD,BOOL,LPCWSTR);
 #define                       OpenSemaphore WINELIB_NAME_AW(OpenSemaphore)
-WINBASEAPI HANDLE      WINAPI OpenThread(DWORD,BOOL,DWORD);
-WINADVAPI  BOOL        WINAPI OpenThreadToken(HANDLE,DWORD,BOOL,PHANDLE);
 WINBASEAPI HANDLE      WINAPI OpenWaitableTimerA(DWORD,BOOL,LPCSTR);
 WINBASEAPI HANDLE      WINAPI OpenWaitableTimerW(DWORD,BOOL,LPCWSTR);
 #define                       OpenWaitableTimer WINELIB_NAME_AW(OpenWaitableTimer)
@@ -2259,7 +2142,6 @@ WINBASEAPI VOID        WINAPI OutputDebugStringW(LPCWSTR);
 WINBASEAPI BOOL        WINAPI PeekNamedPipe(HANDLE,PVOID,DWORD,PDWORD,PDWORD,PDWORD);
 WINBASEAPI BOOL        WINAPI PostQueuedCompletionStatus(HANDLE,DWORD,ULONG_PTR,LPOVERLAPPED);
 WINBASEAPI DWORD       WINAPI PrepareTape(HANDLE,DWORD,BOOL);
-WINBASEAPI BOOL        WINAPI ProcessIdToSessionId(DWORD,DWORD*);
 WINADVAPI  BOOL        WINAPI PrivilegeCheck(HANDLE,PPRIVILEGE_SET,LPBOOL);
 WINADVAPI  BOOL        WINAPI PrivilegedServiceAuditAlarmA(LPCSTR,LPCSTR,HANDLE,PPRIVILEGE_SET,BOOL);
 WINADVAPI  BOOL        WINAPI PrivilegedServiceAuditAlarmW(LPCWSTR,LPCWSTR,HANDLE,PPRIVILEGE_SET,BOOL);
@@ -2280,8 +2162,6 @@ WINBASEAPI BOOL        WINAPI QueryMemoryResourceNotification(HANDLE,PBOOL);
 WINBASEAPI BOOL        WINAPI QueryPerformanceCounter(LARGE_INTEGER*);
 WINBASEAPI BOOL        WINAPI QueryPerformanceFrequency(LARGE_INTEGER*);
 WINBASEAPI BOOL        WINAPI QueryUmsThreadInformation(PUMS_CONTEXT,UMS_THREAD_INFO_CLASS,PVOID,ULONG,PULONG);
-WINBASEAPI DWORD       WINAPI QueueUserAPC(PAPCFUNC,HANDLE,ULONG_PTR);
-WINBASEAPI DWORD       WINAPI QueueUserAPC2(PAPCFUNC,HANDLE,ULONG_PTR,QUEUE_USER_APC_FLAGS);
 WINBASEAPI BOOL        WINAPI QueueUserWorkItem(LPTHREAD_START_ROUTINE,PVOID,ULONG);
 WINBASEAPI void        WINAPI RaiseException(DWORD,DWORD,DWORD,const ULONG_PTR *);
 WINBASEAPI HANDLE      WINAPI ReOpenFile(HANDLE,DWORD,DWORD,DWORD);
@@ -2321,7 +2201,6 @@ WINBASEAPI BOOL        WINAPI RequestWakeupLatency(LATENCY_TIME latency);
 #define                       ReportEvent WINELIB_NAME_AW(ReportEvent)
 WINBASEAPI BOOL        WINAPI ResetEvent(HANDLE);
 WINBASEAPI UINT        WINAPI ResetWriteWatch(LPVOID,SIZE_T);
-WINBASEAPI DWORD       WINAPI ResumeThread(HANDLE);
 WINADVAPI  BOOL        WINAPI RevertToSelf(void);
 WINBASEAPI DWORD       WINAPI SearchPathA(LPCSTR,LPCSTR,LPCSTR,DWORD,LPSTR,LPSTR*);
 WINBASEAPI DWORD       WINAPI SearchPathW(LPCWSTR,LPCWSTR,LPCWSTR,DWORD,LPWSTR,LPWSTR*);
@@ -2372,13 +2251,9 @@ WINBASEAPI BOOL        WINAPI SetInformationJobObject(HANDLE,JOBOBJECTINFOCLASS,
 WINADVAPI  BOOL        WINAPI SetKernelObjectSecurity(HANDLE,SECURITY_INFORMATION,PSECURITY_DESCRIPTOR);
 WINBASEAPI BOOL        WINAPI SetMailslotInfo(HANDLE,DWORD);
 WINBASEAPI BOOL        WINAPI SetNamedPipeHandleState(HANDLE,LPDWORD,LPDWORD,LPDWORD);
-WINBASEAPI BOOL        WINAPI SetPriorityClass(HANDLE,DWORD);
 WINADVAPI  BOOL        WINAPI SetPrivateObjectSecurity(SECURITY_INFORMATION,PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR*,PGENERIC_MAPPING,HANDLE);
 WINADVAPI  BOOL        WINAPI SetPrivateObjectSecurityEx(SECURITY_INFORMATION,PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR*,ULONG,PGENERIC_MAPPING,HANDLE);
 WINBASEAPI BOOL        WINAPI SetProcessAffinityMask(HANDLE,DWORD_PTR);
-WINBASEAPI BOOL        WINAPI SetProcessInformation(HANDLE,PROCESS_INFORMATION_CLASS,LPVOID,DWORD);
-WINBASEAPI BOOL        WINAPI SetProcessPriorityBoost(HANDLE,BOOL);
-WINBASEAPI BOOL        WINAPI SetProcessShutdownParameters(DWORD,DWORD);
 WINBASEAPI BOOL        WINAPI SetProcessWorkingSetSize(HANDLE,SIZE_T,SIZE_T);
 WINBASEAPI BOOL        WINAPI SetProcessWorkingSetSizeEx(HANDLE,SIZE_T,SIZE_T,DWORD);
 WINBASEAPI BOOL        WINAPI SetSearchPathMode(DWORD);
@@ -2394,15 +2269,9 @@ WINBASEAPI BOOL        WINAPI SetSystemPowerState(BOOL,BOOL);
 WINBASEAPI DWORD       WINAPI SetTapeParameters(HANDLE,DWORD,LPVOID);
 WINBASEAPI DWORD       WINAPI SetTapePosition(HANDLE,DWORD,DWORD,DWORD,DWORD,BOOL);
 WINBASEAPI DWORD_PTR   WINAPI SetThreadAffinityMask(HANDLE,DWORD_PTR);
-WINBASEAPI BOOL        WINAPI SetThreadContext(HANDLE,const CONTEXT *);
 WINBASEAPI BOOL        WINAPI SetThreadErrorMode(DWORD,LPDWORD);
 WINBASEAPI DWORD       WINAPI SetThreadExecutionState(EXECUTION_STATE);
 WINBASEAPI BOOL        WINAPI SetThreadGroupAffinity(HANDLE, const GROUP_AFFINITY *, GROUP_AFFINITY *);
-WINBASEAPI DWORD       WINAPI SetThreadIdealProcessor(HANDLE,DWORD);
-WINBASEAPI BOOL        WINAPI SetThreadIdealProcessorEx(HANDLE, PROCESSOR_NUMBER *, PROCESSOR_NUMBER *);
-WINBASEAPI BOOL        WINAPI SetThreadPriority(HANDLE,INT);
-WINBASEAPI BOOL        WINAPI SetThreadPriorityBoost(HANDLE,BOOL);
-WINADVAPI  BOOL        WINAPI SetThreadToken(PHANDLE,HANDLE);
 WINBASEAPI HANDLE      WINAPI SetTimerQueueTimer(HANDLE,WAITORTIMERCALLBACK,PVOID,DWORD,DWORD,BOOL);
 WINADVAPI  BOOL        WINAPI SetTokenInformation(HANDLE,TOKEN_INFORMATION_CLASS,LPVOID,DWORD);
 WINBASEAPI LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER);
@@ -2423,17 +2292,9 @@ WINBASEAPI VOID        WINAPI Sleep(DWORD);
 WINBASEAPI BOOL        WINAPI SleepConditionVariableCS(PCONDITION_VARIABLE,PCRITICAL_SECTION,DWORD);
 WINBASEAPI BOOL        WINAPI SleepConditionVariableSRW(PCONDITION_VARIABLE,PSRWLOCK,DWORD,ULONG);
 WINBASEAPI DWORD       WINAPI SleepEx(DWORD,BOOL);
-WINBASEAPI DWORD       WINAPI SuspendThread(HANDLE);
 WINBASEAPI void        WINAPI SwitchToFiber(LPVOID);
-WINBASEAPI BOOL        WINAPI SwitchToThread(void);
 WINBASEAPI BOOL        WINAPI SystemTimeToFileTime(const SYSTEMTIME*,LPFILETIME);
 WINBASEAPI BOOL        WINAPI TerminateJobObject(HANDLE,UINT);
-WINBASEAPI BOOL        WINAPI TerminateProcess(HANDLE,UINT);
-WINBASEAPI BOOL        WINAPI TerminateThread(HANDLE,DWORD);
-WINBASEAPI DWORD       WINAPI TlsAlloc(void);
-WINBASEAPI BOOL        WINAPI TlsFree(DWORD);
-WINBASEAPI LPVOID      WINAPI TlsGetValue(DWORD);
-WINBASEAPI BOOL        WINAPI TlsSetValue(DWORD,LPVOID);
 WINBASEAPI BOOL        WINAPI TransactNamedPipe(HANDLE,LPVOID,DWORD,LPVOID,DWORD,LPDWORD,LPOVERLAPPED);
 WINBASEAPI BOOL        WINAPI TransmitCommChar(HANDLE,CHAR);
 WINBASEAPI BOOLEAN     WINAPI TryAcquireSRWLockExclusive(PSRWLOCK);
@@ -2452,7 +2313,6 @@ WINBASEAPI HRESULT     WINAPI UnregisterApplicationRecoveryCallback(void);
 WINBASEAPI HRESULT     WINAPI UnregisterApplicationRestart(void);
 WINBASEAPI BOOL        WINAPI UnregisterWait(HANDLE);
 WINBASEAPI BOOL        WINAPI UnregisterWaitEx(HANDLE,HANDLE);
-WINBASEAPI BOOL        WINAPI UpdateProcThreadAttribute(struct _PROC_THREAD_ATTRIBUTE_LIST*,DWORD,DWORD_PTR,void*,SIZE_T,void*,SIZE_T*);
 WINBASEAPI BOOL        WINAPI UpdateResourceA(HANDLE,LPCSTR,LPCSTR,WORD,LPVOID,DWORD);
 WINBASEAPI BOOL        WINAPI UpdateResourceW(HANDLE,LPCWSTR,LPCWSTR,WORD,LPVOID,DWORD);
 #define                       UpdateResource WINELIB_NAME_AW(UpdateResource)
@@ -2645,34 +2505,7 @@ extern char * CDECL wine_get_unix_file_name( const WCHAR * ) __WINE_DEALLOC(Heap
 extern WCHAR * CDECL wine_get_dos_file_name( const char * ) __WINE_DEALLOC(HeapFree,3) __WINE_MALLOC;
 
 
-#ifdef WINE_UNIX_LIB
-
-#define GetCurrentProcess()   NtCurrentProcess()
-#define GetCurrentThread()    NtCurrentThread()
-#define GetCurrentProcessId() HandleToULong(PsGetCurrentProcessId())
-#define GetCurrentThreadId()  HandleToULong(PsGetCurrentThreadId())
-
-#elif defined(__WINESRC__)
-
-static FORCEINLINE HANDLE WINAPI GetCurrentProcess(void)
-{
-    return (HANDLE)~(ULONG_PTR)0;
-}
-
-static FORCEINLINE DWORD WINAPI GetCurrentProcessId(void)
-{
-    return HandleToULong( ((HANDLE *)NtCurrentTeb())[8] );
-}
-
-static FORCEINLINE HANDLE WINAPI GetCurrentThread(void)
-{
-    return (HANDLE)~(ULONG_PTR)1;
-}
-
-static FORCEINLINE DWORD WINAPI GetCurrentThreadId(void)
-{
-    return HandleToULong( ((HANDLE *)NtCurrentTeb())[9] );
-}
+#ifdef __WINESRC__
 
 static FORCEINLINE DWORD WINAPI GetLastError(void)
 {
@@ -2691,31 +2524,12 @@ static FORCEINLINE void WINAPI SetLastError( DWORD err )
 
 #else  /* __WINESRC__ */
 
-WINBASEAPI HANDLE      WINAPI GetCurrentProcess(void);
-WINBASEAPI DWORD       WINAPI GetCurrentProcessId(void);
-WINBASEAPI HANDLE      WINAPI GetCurrentThread(void);
-WINBASEAPI DWORD       WINAPI GetCurrentThreadId(void);
 WINBASEAPI DWORD       WINAPI GetLastError(void);
 WINBASEAPI HANDLE      WINAPI GetProcessHeap(void);
 WINBASEAPI VOID        WINAPI SetLastError(DWORD);
 
 #endif  /* __WINESRC__ */
 
-static FORCEINLINE HANDLE WINAPI GetCurrentProcessToken(void)
-{
-    return (HANDLE)~(ULONG_PTR)3;
-}
-
-static FORCEINLINE HANDLE WINAPI GetCurrentThreadToken(void)
-{
-    return (HANDLE)~(ULONG_PTR)4;
-}
-
-static FORCEINLINE HANDLE WINAPI GetCurrentThreadEffectiveToken(void)
-{
-    return (HANDLE)~(ULONG_PTR)5;
-}
-
 /* WinMain(entry point) must be declared in winbase.h. */
 /* If this is not declared, we cannot compile many sources written with C++. */
 int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int);
diff --git a/include/wincrypt.h b/include/wincrypt.h
index 8d724d92092c..362b88b9d9ed 100644
--- a/include/wincrypt.h
+++ b/include/wincrypt.h
@@ -219,9 +219,12 @@ typedef struct _CRYPT_KEY_PROV_INFO {
 } CRYPT_KEY_PROV_INFO, *PCRYPT_KEY_PROV_INFO;
 
 typedef struct _CERT_KEY_CONTEXT {
-    DWORD      cbSize;
-    HCRYPTPROV hCryptProv;
-    DWORD      dwKeySpec;
+    DWORD cbSize;
+    union {
+        HCRYPTPROV        hCryptProv;
+        NCRYPT_KEY_HANDLE hNCryptKey;
+    } DUMMYUNIONNAME;
+    DWORD dwKeySpec;
 } CERT_KEY_CONTEXT, *PCERT_KEY_CONTEXT;
 
 typedef struct _CERT_PUBLIC_KEY_INFO {
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 838ab5950789..751bc09da744 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -1022,17 +1022,28 @@ typedef volatile struct
     unsigned __int64     keystate_serial;
 } input_shm_t;
 
-typedef volatile struct
+struct class_info
 {
     atom_t               atom;
     unsigned int         style;
     unsigned int         cls_extra;
     unsigned int         win_extra;
+    user_handle_t        cursor;
+    user_handle_t        background;
+    user_handle_t        icon;
+    user_handle_t        icon_small;
     mod_handle_t         instance;
+    client_ptr_t         wndproc;
+    client_ptr_t         menu_name;
+};
+
+typedef volatile struct
+{
     data_size_t          name_offset;
     data_size_t          name_len;
     WCHAR                name[MAX_ATOM_LEN];
     unsigned short       __pad;
+    struct class_info    info;
     char                 extra[];
 } class_shm_t;
 
@@ -1040,6 +1051,8 @@ typedef volatile struct
 {
     struct obj_locator   class;
     unsigned int         dpi_context;
+    unsigned int         fnid;
+    data_size_t          private_size;
 } window_shm_t;
 
 typedef volatile union
@@ -3586,6 +3599,20 @@ struct set_window_info_reply
 
 
 
+struct set_window_fnid_request
+{
+    struct request_header __header;
+    user_handle_t  handle;
+    atom_t         atom;
+    unsigned int   fnid;
+};
+struct set_window_fnid_reply
+{
+    struct reply_header __header;
+};
+
+
+
 struct set_parent_request
 {
     struct request_header __header;
@@ -4505,13 +4532,12 @@ struct create_class_request
     struct request_header __header;
     int            local;
     atom_t         atom;
-    unsigned int   style;
-    mod_handle_t   instance;
+    char __pad_20[4];
     client_ptr_t   client_ptr;
-    short int      cls_extra;
-    short int      win_extra;
     data_size_t    name_offset;
+    /* VARARG(info,class_info); */
     /* VARARG(name,unicode_str); */
+    char __pad_36[4];
 };
 struct create_class_reply
 {
@@ -4534,6 +4560,9 @@ struct destroy_class_reply
 {
     struct reply_header __header;
     client_ptr_t   client_ptr;
+    user_handle_t  background;
+    char __pad_20[4];
+    client_ptr_t   menu_name;
 };
 
 
@@ -6316,6 +6345,7 @@ enum request
     REQ_get_window_info,
     REQ_init_window_info,
     REQ_set_window_info,
+    REQ_set_window_fnid,
     REQ_set_parent,
     REQ_get_window_parents,
     REQ_get_window_list,
@@ -6629,6 +6659,7 @@ union generic_request
     struct get_window_info_request get_window_info_request;
     struct init_window_info_request init_window_info_request;
     struct set_window_info_request set_window_info_request;
+    struct set_window_fnid_request set_window_fnid_request;
     struct set_parent_request set_parent_request;
     struct get_window_parents_request get_window_parents_request;
     struct get_window_list_request get_window_list_request;
@@ -6940,6 +6971,7 @@ union generic_reply
     struct get_window_info_reply get_window_info_reply;
     struct init_window_info_reply init_window_info_reply;
     struct set_window_info_reply set_window_info_reply;
+    struct set_window_fnid_reply set_window_fnid_reply;
     struct set_parent_reply set_parent_reply;
     struct get_window_parents_reply get_window_parents_reply;
     struct get_window_list_reply get_window_list_reply;
@@ -7099,6 +7131,6 @@ union generic_reply
     struct d3dkmt_mutex_release_reply d3dkmt_mutex_release_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 934
+#define SERVER_PROTOCOL_VERSION 942
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/include/winnt.h b/include/winnt.h
index 3381b65d932d..2dc1aaddddea 100644
--- a/include/winnt.h
+++ b/include/winnt.h
@@ -7127,27 +7127,241 @@ typedef enum _FIRMWARE_TYPE
 #define BitScanReverse _BitScanReverse
 #define BitScanReverse64 _BitScanReverse64
 #define InterlockedAdd _InlineInterlockedAdd
+#define InterlockedAdd64 _InlineInterlockedAdd64
 #define InterlockedAnd _InterlockedAnd
+#define InterlockedAnd8 _InterlockedAnd8
+#define InterlockedAnd16 _InterlockedAnd16
 #define InterlockedAnd64 _InterlockedAnd64
 #define InterlockedCompareExchange _InterlockedCompareExchange
+#define InterlockedCompareExchange8 _InterlockedCompareExchange8
+#define InterlockedCompareExchange16 _InterlockedCompareExchange16
 #define InterlockedCompareExchange64 _InterlockedCompareExchange64
 #define InterlockedCompareExchangePointer _InterlockedCompareExchangePointer
 #define InterlockedDecrement _InterlockedDecrement
 #define InterlockedDecrement16 _InterlockedDecrement16
 #define InterlockedDecrement64 _InterlockedDecrement64
 #define InterlockedExchange _InterlockedExchange
+#define InterlockedExchange8 _InterlockedExchange8
+#define InterlockedExchange16 _InterlockedExchange16
+#define InterlockedExchange64 _InterlockedExchange64
+#define InterlockedExchangePointer _InterlockedExchangePointer
 #define InterlockedExchangeAdd _InterlockedExchangeAdd
+#define InterlockedExchangeAdd8 _InterlockedExchangeAdd8
 #define InterlockedExchangeAdd16 _InterlockedExchangeAdd16
 #define InterlockedExchangeAdd64 _InterlockedExchangeAdd64
-#define InterlockedExchangePointer _InterlockedExchangePointer
 #define InterlockedIncrement _InterlockedIncrement
 #define InterlockedIncrement16 _InterlockedIncrement16
 #define InterlockedIncrement64 _InterlockedIncrement64
 #define InterlockedOr _InterlockedOr
+#define InterlockedOr8 _InterlockedOr8
+#define InterlockedOr16 _InterlockedOr16
 #define InterlockedOr64 _InterlockedOr64
 #define InterlockedXor _InterlockedXor
+#define InterlockedXor8 _InterlockedXor8
+#define InterlockedXor16 _InterlockedXor16
 #define InterlockedXor64 _InterlockedXor64
 
+#if defined(__arm__) || defined(__aarch64__) || defined(__arm64ec__)
+#define InterlockedAddAcquire _InterlockedAdd_acq
+#define InterlockedAddNoFence _InterlockedAdd_nf
+#define InterlockedAddRelease _InterlockedAdd_rel
+#define InterlockedAddAcquire64 _InterlockedAdd64_acq
+#define InterlockedAddNoFence64 _InterlockedAdd64_nf
+#define InterlockedAddRelease64 _InterlockedAdd64_rel
+#define InterlockedAndAcquire _InterlockedAnd_acq
+#define InterlockedAndNoFence _InterlockedAnd_nf
+#define InterlockedAndRelease _InterlockedAnd_rel
+#define InterlockedAndAcquire8 _InterlockedAnd8_acq
+#define InterlockedAndNoFence8 _InterlockedAnd8_nf
+#define InterlockedAndRelease8 _InterlockedAnd8_rel
+#define InterlockedAndAcquire16 _InterlockedAnd16_acq
+#define InterlockedAndNoFence16 _InterlockedAnd16_nf
+#define InterlockedAndRelease16 _InterlockedAnd16_rel
+#define InterlockedAndAcquire64 _InterlockedAnd64_acq
+#define InterlockedAndRelease64 _InterlockedAnd64_rel
+#define InterlockedAndNoFence64 _InterlockedAnd64_nf
+#define InterlockedAnd64Acquire _InterlockedAnd64_acq
+#define InterlockedAnd64NoFence _InterlockedAnd64_nf
+#define InterlockedAnd64Release _InterlockedAnd64_rel
+#define InterlockedCompareExchangeAcquire _InterlockedCompareExchange_acq
+#define InterlockedCompareExchangeNoFence _InterlockedCompareExchange_nf
+#define InterlockedCompareExchangeRelease _InterlockedCompareExchange_rel
+#define InterlockedCompareExchangeAcquire8 _InterlockedCompareExchange8_acq
+#define InterlockedCompareExchangeNoFence8 _InterlockedCompareExchange8_nf
+#define InterlockedCompareExchangeRelease8 _InterlockedCompareExchange8_rel
+#define InterlockedCompareExchangeAcquire16 _InterlockedCompareExchange16_acq
+#define InterlockedCompareExchangeNoFence16 _InterlockedCompareExchange16_nf
+#define InterlockedCompareExchangeRelease16 _InterlockedCompareExchange16_rel
+#define InterlockedCompareExchangeAcquire64 _InterlockedCompareExchange64_acq
+#define InterlockedCompareExchangeNoFence64 _InterlockedCompareExchange64_nf
+#define InterlockedCompareExchangeRelease64 _InterlockedCompareExchange64_rel
+#ifdef _WIN64
+#define InterlockedCompareExchange128 _InterlockedCompareExchange128
+#define InterlockedCompareExchangeAcquire128 _InterlockedCompareExchange128_acq
+#define InterlockedCompareExchangeNoFence128 _InterlockedCompareExchange128_nf
+#define InterlockedCompareExchangeRelease128 _InterlockedCompareExchange128_rel
+#endif
+#define InterlockedCompareExchangePointerAcquire _InterlockedCompareExchangePointer_acq
+#define InterlockedCompareExchangePointerNoFence _InterlockedCompareExchangePointer_nf
+#define InterlockedCompareExchangePointerRelease _InterlockedCompareExchangePointer_rel
+#define InterlockedDecrementAcquire _InterlockedDecrement_acq
+#define InterlockedDecrementNoFence _InterlockedDecrement_nf
+#define InterlockedDecrementRelease _InterlockedDecrement_rel
+#define InterlockedDecrementAcquire16 _InterlockedDecrement16_acq
+#define InterlockedDecrementNoFence16 _InterlockedDecrement16_nf
+#define InterlockedDecrementRelease16 _InterlockedDecrement16_rel
+#define InterlockedDecrementAcquire64 _InterlockedDecrement64_acq
+#define InterlockedDecrementNoFence64 _InterlockedDecrement64_nf
+#define InterlockedDecrementRelease64 _InterlockedDecrement64_rel
+#define InterlockedExchangeAcquire _InterlockedExchange_acq
+#define InterlockedExchangeNoFence _InterlockedExchange_nf
+#define InterlockedExchangeAcquire8 _InterlockedExchange8_acq
+#define InterlockedExchangeNoFence8 _InterlockedExchange8_nf
+#define InterlockedExchangeAcquire16 _InterlockedExchange16_acq
+#define InterlockedExchangeNoFence16 _InterlockedExchange16_nf
+#define InterlockedExchangeAcquire64 _InterlockedExchange64_acq
+#define InterlockedExchangeNoFence64 _InterlockedExchange64_nf
+#define InterlockedExchangePointerAcquire _InterlockedExchangePointer_acq
+#define InterlockedExchangePointerNoFence _InterlockedExchangePointer_nf
+#define InterlockedExchangeAddAcquire _InterlockedExchangeAdd_acq
+#define InterlockedExchangeAddNoFence _InterlockedExchangeAdd_nf
+#define InterlockedExchangeAddRelease _InterlockedExchangeAdd_rel
+#define InterlockedExchangeAddAcquire64 _InterlockedExchangeAdd64_acq
+#define InterlockedExchangeAddNoFence64 _InterlockedExchangeAdd64_nf
+#define InterlockedExchangeAddRelease64 _InterlockedExchangeAdd64_rel
+#define InterlockedIncrementAcquire _InterlockedIncrement_acq
+#define InterlockedIncrementNoFence _InterlockedIncrement_nf
+#define InterlockedIncrementRelease _InterlockedIncrement_rel
+#define InterlockedIncrementAcquire16 _InterlockedIncrement16_acq
+#define InterlockedIncrementNoFence16 _InterlockedIncrement16_nf
+#define InterlockedIncrementRelease16 _InterlockedIncrement16_rel
+#define InterlockedIncrementAcquire64 _InterlockedIncrement64_acq
+#define InterlockedIncrementNoFence64 _InterlockedIncrement64_nf
+#define InterlockedIncrementRelease64 _InterlockedIncrement64_rel
+#define InterlockedOrAcquire _InterlockedOr_acq
+#define InterlockedOrNoFence _InterlockedOr_nf
+#define InterlockedOrRelease _InterlockedOr_rel
+#define InterlockedOrAcquire8 _InterlockedOr8_acq
+#define InterlockedOrNoFence8 _InterlockedOr8_nf
+#define InterlockedOrRelease8 _InterlockedOr8_rel
+#define InterlockedOrAcquire16 _InterlockedOr16_acq
+#define InterlockedOrNoFence16 _InterlockedOr16_nf
+#define InterlockedOrRelease16 _InterlockedOr16_rel
+#define InterlockedOrAcquire64 _InterlockedOr64_acq
+#define InterlockedOrNoFence64 _InterlockedOr64_nf
+#define InterlockedOrRelease64 _InterlockedOr64_rel
+#define InterlockedXorAcquire _InterlockedXor_acq
+#define InterlockedXorNoFence _InterlockedXor_nf
+#define InterlockedXorRelease _InterlockedXor_rel
+#define InterlockedXorAcquire8 _InterlockedXor8_acq
+#define InterlockedXorNoFence8 _InterlockedXor8_nf
+#define InterlockedXorRelease8 _InterlockedXor8_rel
+#define InterlockedXorAcquire16 _InterlockedXor16_acq
+#define InterlockedXorNoFence16 _InterlockedXor16_nf
+#define InterlockedXorRelease16 _InterlockedXor16_rel
+#define InterlockedXorAcquire64 _InterlockedXor64_acq
+#define InterlockedXorNoFence64 _InterlockedXor64_nf
+#define InterlockedXorRelease64 _InterlockedXor64_rel
+#else
+#define InterlockedAddAcquire _InlineInterlockedAdd
+#define InterlockedAddNoFence _InlineInterlockedAdd
+#define InterlockedAddRelease _InlineInterlockedAdd
+#define InterlockedAddAcquire64 _InlineInterlockedAdd64
+#define InterlockedAddNoFence64 _InlineInterlockedAdd64
+#define InterlockedAddRelease64 _InlineInterlockedAdd64
+#define InterlockedAndAcquire _InterlockedAnd
+#define InterlockedAndNoFence _InterlockedAnd
+#define InterlockedAndRelease _InterlockedAnd
+#define InterlockedAndAcquire8 _InterlockedAnd8
+#define InterlockedAndNoFence8 _InterlockedAnd8
+#define InterlockedAndRelease8 _InterlockedAnd8
+#define InterlockedAndAcquire16 InterlockedAnd16
+#define InterlockedAndNoFence16 InterlockedAnd16
+#define InterlockedAndRelease16 InterlockedAnd16
+#define InterlockedAnd64Acquire _InterlockedAnd64
+#define InterlockedAnd64NoFence _InterlockedAnd64
+#define InterlockedAnd64Release _InterlockedAnd64
+#define InterlockedCompareExchangeAcquire _InterlockedCompareExchange
+#define InterlockedCompareExchangeNoFence _InterlockedCompareExchange
+#define InterlockedCompareExchangeRelease _InterlockedCompareExchange
+#define InterlockedCompareExchangeAcquire8 _InterlockedCompareExchange8
+#define InterlockedCompareExchangeNoFence8 _InterlockedCompareExchange8
+#define InterlockedCompareExchangeRelease8 _InterlockedCompareExchange8
+#define InterlockedCompareExchangeAcquire16 _InterlockedCompareExchange16
+#define InterlockedCompareExchangeNoFence16 _InterlockedCompareExchange16
+#define InterlockedCompareExchangeRelease16 _InterlockedCompareExchange16
+#define InterlockedCompareExchangeAcquire64 InterlockedCompareExchange64
+#define InterlockedCompareExchangeNoFence64 InterlockedCompareExchange64
+#define InterlockedCompareExchangeRelease64 InterlockedCompareExchange64
+#ifdef _WIN64
+#define InterlockedCompareExchange128 _InterlockedCompareExchange128
+#define InterlockedCompareExchangeAcquire128 _InterlockedCompareExchange128
+#define InterlockedCompareExchangeNoFence128 _InterlockedCompareExchange128
+#define InterlockedCompareExchangeRelease128 _InterlockedCompareExchange128
+#endif
+#define InterlockedCompareExchangePointerAcquire _InterlockedCompareExchangePointer
+#define InterlockedCompareExchangePointerNoFence _InterlockedCompareExchangePointer
+#define InterlockedCompareExchangePointerRelease _InterlockedCompareExchangePointer
+#define InterlockedDecrementAcquire _InterlockedDecrement
+#define InterlockedDecrementNoFence _InterlockedDecrement
+#define InterlockedDecrementRelease _InterlockedDecrement
+#define InterlockedDecrementAcquire16 _InterlockedDecrement16
+#define InterlockedDecrementNoFence16 _InterlockedDecrement16
+#define InterlockedDecrementRelease16 _InterlockedDecrement16
+#define InterlockedDecrementAcquire64 _InterlockedDecrement64
+#define InterlockedDecrementNoFence64 _InterlockedDecrement64
+#define InterlockedDecrementRelease64 _InterlockedDecrement64
+#define InterlockedExchangeAcquire _InterlockedExchange
+#define InterlockedExchangeNoFence _InterlockedExchange
+#define InterlockedExchangeAcquire8 InterlockedExchange8
+#define InterlockedExchangeNoFence8 InterlockedExchange8
+#define InterlockedExchangeAcquire16 InterlockedExchange16
+#define InterlockedExchangeNoFence16 InterlockedExchange16
+#define InterlockedExchangeAcquire64 InterlockedExchange64
+#define InterlockedExchangeNoFence64 InterlockedExchange64
+#define InterlockedExchangePointerNoFence _InterlockedExchangePointer
+#define InterlockedExchangePointerAcquire _InterlockedExchangePointer
+#define InterlockedExchangeAddAcquire _InterlockedExchangeAdd
+#define InterlockedExchangeAddNoFence _InterlockedExchangeAdd
+#define InterlockedExchangeAddRelease _InterlockedExchangeAdd
+#define InterlockedExchangeAddAcquire64 _InterlockedExchangeAdd64
+#define InterlockedExchangeAddNoFence64 _InterlockedExchangeAdd64
+#define InterlockedExchangeAddRelease64 _InterlockedExchangeAdd64
+#define InterlockedIncrementAcquire _InterlockedIncrement
+#define InterlockedIncrementNoFence _InterlockedIncrement
+#define InterlockedIncrementRelease _InterlockedIncrement
+#define InterlockedIncrementAcquire16 _InterlockedIncrement16
+#define InterlockedIncrementNoFence16 _InterlockedIncrement16
+#define InterlockedIncrementRelease16 _InterlockedIncrement16
+#define InterlockedIncrementAcquire64 _InterlockedIncrement64
+#define InterlockedIncrementNoFence64 _InterlockedIncrement64
+#define InterlockedIncrementRelease64 _InterlockedIncrement64
+#define InterlockedOrAcquire _InterlockedOr
+#define InterlockedOrNoFence _InterlockedOr
+#define InterlockedOrRelease _InterlockedOr
+#define InterlockedOrAcquire8 _InterlockedOr8
+#define InterlockedOrNoFence8 _InterlockedOr8
+#define InterlockedOrRelease8 _InterlockedOr8
+#define InterlockedOrAcquire16 InterlockedOr16
+#define InterlockedOrNoFence16 InterlockedOr16
+#define InterlockedOrRelease16 InterlockedOr16
+#define InterlockedOr64Acquire _InterlockedOr64
+#define InterlockedOr64NoFence _InterlockedOr64
+#define InterlockedOr64Release _InterlockedOr64
+#define InterlockedXorAcquire _InterlockedXor
+#define InterlockedXorNoFence _InterlockedXor
+#define InterlockedXorRelease _InterlockedXor
+#define InterlockedXorAcquire8 _InterlockedXor8
+#define InterlockedXorNoFence8 _InterlockedXor8
+#define InterlockedXorRelease8 _InterlockedXor8
+#define InterlockedXorAcquire16 _InterlockedXor16
+#define InterlockedXorNoFence16 _InterlockedXor16
+#define InterlockedXorRelease16 _InterlockedXor16
+#define InterlockedXor64Acquire _InterlockedXor64
+#define InterlockedXor64NoFence _InterlockedXor64
+#define InterlockedXor64Release _InterlockedXor64
+#endif
+
 #ifdef _MSC_VER
 
 BOOLEAN   _BitScanForward(unsigned long*,unsigned long);
@@ -7460,6 +7674,16 @@ static FORCEINLINE LONG WINAPI InterlockedAnd( LONG volatile *dest, LONG val )
     return __sync_fetch_and_and( dest, val );
 }
 
+static FORCEINLINE char WINAPI InterlockedAnd8( char volatile *dest, char val )
+{
+    return __sync_fetch_and_and( dest, val );
+}
+
+static FORCEINLINE short WINAPI InterlockedAnd16( short volatile *dest, short val )
+{
+    return __sync_fetch_and_and( dest, val );
+}
+
 static FORCEINLINE LONGLONG WINAPI InterlockedAnd64( LONGLONG volatile *dest, LONGLONG val )
 {
     return __sync_fetch_and_and( dest, val );
@@ -7470,6 +7694,16 @@ static FORCEINLINE LONG WINAPI InterlockedCompareExchange( LONG volatile *dest,
     return __sync_val_compare_and_swap( dest, compare, xchg );
 }
 
+static FORCEINLINE char WINAPI InterlockedCompareExchange8( char volatile *dest, char xchg, char compare )
+{
+    return __sync_val_compare_and_swap( dest, compare, xchg );
+}
+
+static FORCEINLINE short WINAPI InterlockedCompareExchange16( short volatile *dest, short xchg, short compare )
+{
+    return __sync_val_compare_and_swap( dest, compare, xchg );
+}
+
 static FORCEINLINE void * WINAPI InterlockedCompareExchangePointer( void *volatile *dest, void *xchg, void *compare )
 {
     return __sync_val_compare_and_swap( dest, compare, xchg );
@@ -7494,11 +7728,44 @@ static FORCEINLINE LONG WINAPI InterlockedExchange( LONG volatile *dest, LONG va
     return ret;
 }
 
+static FORCEINLINE char WINAPI InterlockedExchange8( char volatile *dest, char val )
+{
+    char ret;
+#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
+    ret = __atomic_exchange_n( dest, val, __ATOMIC_SEQ_CST );
+#elif defined(__i386__) || defined(__x86_64__)
+    __asm__ __volatile__( "lock; xchgb %0,(%1)"
+                          : "=r" (ret) :"r" (dest), "0" (val) : "memory" );
+#else
+    do ret = *dest; while (!__sync_bool_compare_and_swap( dest, ret, val ));
+#endif
+    return ret;
+}
+
+static FORCEINLINE short WINAPI InterlockedExchange16( short volatile *dest, short val )
+{
+    short ret;
+#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
+    ret = __atomic_exchange_n( dest, val, __ATOMIC_SEQ_CST );
+#elif defined(__i386__) || defined(__x86_64__)
+    __asm__ __volatile__( "lock; xchgw %0,(%1)"
+                          : "=r" (ret) :"r" (dest), "0" (val) : "memory" );
+#else
+    do ret = *dest; while (!__sync_bool_compare_and_swap( dest, ret, val ));
+#endif
+    return ret;
+}
+
 static FORCEINLINE LONG WINAPI InterlockedExchangeAdd( LONG volatile *dest, LONG incr )
 {
     return __sync_fetch_and_add( dest, incr );
 }
 
+static FORCEINLINE char WINAPI InterlockedExchangeAdd8( char volatile *dest, char incr )
+{
+    return __sync_fetch_and_add( dest, incr );
+}
+
 static FORCEINLINE short WINAPI InterlockedExchangeAdd16( short volatile *dest, short incr )
 {
     return __sync_fetch_and_add( dest, incr );
@@ -7559,6 +7826,16 @@ static FORCEINLINE LONG WINAPI InterlockedOr( LONG volatile *dest, LONG val )
     return __sync_fetch_and_or( dest, val );
 }
 
+static FORCEINLINE char WINAPI InterlockedOr8( char volatile *dest, char val )
+{
+    return __sync_fetch_and_or( dest, val );
+}
+
+static FORCEINLINE short WINAPI InterlockedOr16( short volatile *dest, short val )
+{
+    return __sync_fetch_and_or( dest, val );
+}
+
 static FORCEINLINE LONGLONG WINAPI InterlockedOr64( LONGLONG volatile *dest, LONGLONG val )
 {
     return __sync_fetch_and_or( dest, val );
@@ -7569,6 +7846,16 @@ static FORCEINLINE LONG WINAPI InterlockedXor( LONG volatile *dest, LONG val )
     return __sync_fetch_and_xor( dest, val );
 }
 
+static FORCEINLINE char WINAPI InterlockedXor8( char volatile *dest, char val )
+{
+    return __sync_fetch_and_xor( dest, val );
+}
+
+static FORCEINLINE short WINAPI InterlockedXor16( short volatile *dest, short val )
+{
+    return __sync_fetch_and_xor( dest, val );
+}
+
 static FORCEINLINE LONGLONG WINAPI InterlockedXor64( LONGLONG volatile *dest, LONGLONG val )
 {
     return __sync_fetch_and_xor( dest, val );
@@ -7579,6 +7866,107 @@ static FORCEINLINE void MemoryBarrier(void)
     __sync_synchronize();
 }
 
+#if defined(__arm__) || defined(__aarch64__)
+
+static FORCEINLINE LONG WINAPI InterlockedAddAcquire( LONG volatile *dest, LONG val ) { return __atomic_add_fetch( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONG WINAPI InterlockedAddNoFence( LONG volatile *dest, LONG val ) { return __atomic_add_fetch( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONG WINAPI InterlockedAddRelease( LONG volatile *dest, LONG val ) { return __atomic_add_fetch( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedAddAcquire64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_add_fetch( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedAddNoFence64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_add_fetch( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedAddRelease64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_add_fetch( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONG WINAPI InterlockedAndAcquire( LONG volatile *dest, LONG val ) { return __atomic_fetch_and( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONG WINAPI InterlockedAndNoFence( LONG volatile *dest, LONG val ) { return __atomic_fetch_and( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONG WINAPI InterlockedAndRelease( LONG volatile *dest, LONG val ) { return __atomic_fetch_and( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE char WINAPI InterlockedAndAcquire8( char volatile *dest, char val ) { return __atomic_fetch_and( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE char WINAPI InterlockedAndNoFence8( char volatile *dest, char val ) { return __atomic_fetch_and( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE char WINAPI InterlockedAndRelease8( char volatile *dest, char val ) { return __atomic_fetch_and( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE short WINAPI InterlockedAndAcquire16( short volatile *dest, short val ) { return __atomic_fetch_and( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE short WINAPI InterlockedAndNoFence16( short volatile *dest, short val ) { return __atomic_fetch_and( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE short WINAPI InterlockedAndRelease16( short volatile *dest, short val ) { return __atomic_fetch_and( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedAndAcquire64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_fetch_and( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedAndNoFence64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_fetch_and( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedAndRelease64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_fetch_and( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONG WINAPI InterlockedCompareExchangeAcquire( LONG volatile *dest, LONG xchg, LONG compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE LONG WINAPI InterlockedCompareExchangeNoFence( LONG volatile *dest, LONG xchg, LONG compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE LONG WINAPI InterlockedCompareExchangeRelease( LONG volatile *dest, LONG xchg, LONG compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE char WINAPI InterlockedCompareExchangeAcquire8( char volatile *dest, char xchg, char compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE char WINAPI InterlockedCompareExchangeNoFence8( char volatile *dest, char xchg, char compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE char WINAPI InterlockedCompareExchangeRelease8( char volatile *dest, char xchg, char compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE short WINAPI InterlockedCompareExchangeAcquire16( short volatile *dest, short xchg, short compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE short WINAPI InterlockedCompareExchangeNoFence16( short volatile *dest, short xchg, short compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE short WINAPI InterlockedCompareExchangeRelease16( short volatile *dest, short xchg, short compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE LONGLONG WINAPI InterlockedCompareExchangeAcquire64( LONGLONG volatile *dest, LONGLONG xchg, LONGLONG compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE LONGLONG WINAPI InterlockedCompareExchangeNoFence64( LONGLONG volatile *dest, LONGLONG xchg, LONGLONG compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE LONGLONG WINAPI InterlockedCompareExchangeRelease64( LONGLONG volatile *dest, LONGLONG xchg, LONGLONG compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED ); return compare; }
+#ifdef _WIN64
+static FORCEINLINE unsigned char InterlockedCompareExchangeAcquire128( volatile __int64 *dest, __int64 xchg_high, __int64 xchg_low, __int64 *compare ) { return __atomic_compare_exchange_n( (__int128 *)dest, (__int128 *)compare, ((__int128)xchg_high << 64) | xchg_low, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED ); }
+static FORCEINLINE unsigned char InterlockedCompareExchangeNoFence128( volatile __int64 *dest, __int64 xchg_high, __int64 xchg_low, __int64 *compare ) { return __atomic_compare_exchange_n( (__int128 *)dest, (__int128 *)compare, ((__int128)xchg_high << 64) | xchg_low, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED ); }
+static FORCEINLINE unsigned char InterlockedCompareExchangeRelease128( volatile __int64 *dest, __int64 xchg_high, __int64 xchg_low, __int64 *compare ) { return __atomic_compare_exchange_n( (__int128 *)dest, (__int128 *)compare, ((__int128)xchg_high << 64) | xchg_low, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED ); }
+#endif
+static FORCEINLINE void * WINAPI InterlockedCompareExchangePointerAcquire( void *volatile *dest, void *xchg, void *compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE void * WINAPI InterlockedCompareExchangePointerNoFence( void *volatile *dest, void *xchg, void *compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE void * WINAPI InterlockedCompareExchangePointerRelease( void *volatile *dest, void *xchg, void *compare ) { __atomic_compare_exchange_n( dest, &compare, xchg, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED ); return compare; }
+static FORCEINLINE LONG WINAPI InterlockedDecrementAcquire( LONG volatile *dest ) { return __atomic_add_fetch( dest, -1, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONG WINAPI InterlockedDecrementNoFence( LONG volatile *dest ) { return __atomic_add_fetch( dest, -1, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONG WINAPI InterlockedDecrementRelease( LONG volatile *dest ) { return __atomic_add_fetch( dest, -1, __ATOMIC_RELEASE ); }
+static FORCEINLINE short WINAPI InterlockedDecrementAcquire16( short volatile *dest ) { return __atomic_add_fetch( dest, -1, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE short WINAPI InterlockedDecrementNoFence16( short volatile *dest ) { return __atomic_add_fetch( dest, -1, __ATOMIC_RELAXED ); }
+static FORCEINLINE short WINAPI InterlockedDecrementRelease16( short volatile *dest ) { return __atomic_add_fetch( dest, -1, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedDecrementAcquire64( LONGLONG volatile *dest ) { return __atomic_add_fetch( dest, -1, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedDecrementNoFence64( LONGLONG volatile *dest ) { return __atomic_add_fetch( dest, -1, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedDecrementRelease64( LONGLONG volatile *dest ) { return __atomic_add_fetch( dest, -1, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONG WINAPI InterlockedExchangeAcquire( LONG volatile *dest, LONG val ) { return __atomic_exchange_n( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONG WINAPI InterlockedExchangeNoFence( LONG volatile *dest, LONG val ) { return __atomic_exchange_n( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE char WINAPI InterlockedExchangeAcquire8( char volatile *dest, char val ) { return __atomic_exchange_n( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE char WINAPI InterlockedExchangeNoFence8( char volatile *dest, char val ) { return __atomic_exchange_n( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE short WINAPI InterlockedExchangeAcquire16( short volatile *dest, short val ) { return __atomic_exchange_n( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE short WINAPI InterlockedExchangeNoFence16( short volatile *dest, short val ) { return __atomic_exchange_n( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedExchangeAcquire64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_exchange_n( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedExchangeNoFence64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_exchange_n( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE void * WINAPI InterlockedExchangePointerAcquire( void *volatile *dest, void *val ) { return __atomic_exchange_n( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE void * WINAPI InterlockedExchangePointerNoFence( void *volatile *dest, void *val ) { return __atomic_exchange_n( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONG WINAPI InterlockedExchangeAddAcquire( LONG volatile *dest, LONG incr ) { return __atomic_fetch_add( dest, incr, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONG WINAPI InterlockedExchangeAddNoFence( LONG volatile *dest, LONG incr ) { return __atomic_fetch_add( dest, incr, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONG WINAPI InterlockedExchangeAddRelease( LONG volatile *dest, LONG incr ) { return __atomic_fetch_add( dest, incr, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedExchangeAddAcquire64( LONGLONG volatile *dest, LONGLONG incr ) { return __atomic_fetch_add( dest, incr, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedExchangeAddNoFence64( LONGLONG volatile *dest, LONGLONG incr ) { return __atomic_fetch_add( dest, incr, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedExchangeAddRelease64( LONGLONG volatile *dest, LONGLONG incr ) { return __atomic_fetch_add( dest, incr, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONG WINAPI InterlockedIncrementAcquire( LONG volatile *dest ) { return __atomic_add_fetch( dest, 1, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONG WINAPI InterlockedIncrementNoFence( LONG volatile *dest ) { return __atomic_add_fetch( dest, 1, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONG WINAPI InterlockedIncrementRelease( LONG volatile *dest ) { return __atomic_add_fetch( dest, 1, __ATOMIC_RELEASE ); }
+static FORCEINLINE short WINAPI InterlockedIncrementAcquire16( short volatile *dest ) { return __atomic_add_fetch( dest, 1, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE short WINAPI InterlockedIncrementNoFence16( short volatile *dest ) { return __atomic_add_fetch( dest, 1, __ATOMIC_RELAXED ); }
+static FORCEINLINE short WINAPI InterlockedIncrementRelease16( short volatile *dest ) { return __atomic_add_fetch( dest, 1, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedIncrementAcquire64( LONGLONG volatile *dest ) { return __atomic_add_fetch( dest, 1, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedIncrementNoFence64( LONGLONG volatile *dest ) { return __atomic_add_fetch( dest, 1, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedIncrementRelease64( LONGLONG volatile *dest ) { return __atomic_add_fetch( dest, 1, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONG WINAPI InterlockedOrAcquire( LONG volatile *dest, LONG val ) { return __atomic_fetch_or( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONG WINAPI InterlockedOrNoFence( LONG volatile *dest, LONG val ) { return __atomic_fetch_or( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONG WINAPI InterlockedOrRelease( LONG volatile *dest, LONG val ) { return __atomic_fetch_or( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE char WINAPI InterlockedOrAcquire8( char volatile *dest, char val ) { return __atomic_fetch_or( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE char WINAPI InterlockedOrNoFence8( char volatile *dest, char val ) { return __atomic_fetch_or( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE char WINAPI InterlockedOrRelease8( char volatile *dest, char val ) { return __atomic_fetch_or( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE short WINAPI InterlockedOrAcquire16( short volatile *dest, short val ) { return __atomic_fetch_or( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE short WINAPI InterlockedOrNoFence16( short volatile *dest, short val ) { return __atomic_fetch_or( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE short WINAPI InterlockedOrRelease16( short volatile *dest, short val ) { return __atomic_fetch_or( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedOrAcquire64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_fetch_or( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedOrNoFence64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_fetch_or( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedOrRelease64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_fetch_or( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONG WINAPI InterlockedXorAcquire( LONG volatile *dest, LONG val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONG WINAPI InterlockedXorNoFence( LONG volatile *dest, LONG val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONG WINAPI InterlockedXorRelease( LONG volatile *dest, LONG val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE char WINAPI InterlockedXorAcquire8( char volatile *dest, char val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE char WINAPI InterlockedXorNoFence8( char volatile *dest, char val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE char WINAPI InterlockedXorRelease8( char volatile *dest, char val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE short WINAPI InterlockedXorAcquire16( short volatile *dest, short val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE short WINAPI InterlockedXorNoFence16( short volatile *dest, short val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE short WINAPI InterlockedXorRelease16( short volatile *dest, short val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_RELEASE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedXorAcquire64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_ACQUIRE ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedXorNoFence64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_RELAXED ); }
+static FORCEINLINE LONGLONG WINAPI InterlockedXorRelease64( LONGLONG volatile *dest, LONGLONG val ) { return __atomic_fetch_xor( dest, val, __ATOMIC_RELEASE ); }
+
+#endif  /* defined(__arm__) || defined(__aarch64__) */
+
 #if defined(__x86_64__) || defined(__i386__)
 /* On x86, Support old GCC with either no or buggy (GCC BZ#81316) __atomic_* support */
 #define __WINE_ATOMIC_LOAD_ACQUIRE(ptr, ret) do { C_ASSERT(sizeof(*(ptr)) <= sizeof(void *)); *(ret) = *(ptr); __asm__ __volatile__( "" ::: "memory" ); } while (0)
@@ -7673,8 +8061,6 @@ static FORCEINLINE DECLSPEC_NORETURN void __fastfail(unsigned int code)
 
 #ifdef _WIN64
 
-#define InterlockedCompareExchange128 _InterlockedCompareExchange128
-
 #if defined(_MSC_VER) && (!defined(__clang__) || !defined(__aarch64__) || __has_builtin(_InterlockedCompareExchange128))
 
 unsigned char _InterlockedCompareExchange128(volatile __int64 *, __int64, __int64, __int64 *);
diff --git a/libs/uuid/uuid.c b/libs/uuid/uuid.c
index dd12c7699221..d1fef42140d0 100644
--- a/libs/uuid/uuid.c
+++ b/libs/uuid/uuid.c
@@ -101,6 +101,7 @@ DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
 #include "directmanipulation.h"
 #include "devguid.h"
 #include "propkey.h"
+#include "thumbcache.h"
 
 #define __WINE_INIT_NPI_MODULEID
 #include "netiodef.h"
@@ -111,6 +112,8 @@ DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
 DEFINE_GUID(IID_ISFHelper,                0x1fe68efb,0x1874,0x9812,0x56,0xdc,0x00,0x00,0x00,0x00,0x00,0x00);
 DEFINE_GUID(IID_IProxyManager,            0x00000008,0x0000,0x0000,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
 DEFINE_GUID(IID_IBindStatusCallbackHolder,0x79eac9cc,0xbaf9,0x11ce,0x8c,0x82,0x00,0xaa,0x00,0x4b,0xa9,0x0b);
+DEFINE_GUID(IID_IEnterActivityWithNoLock, 0xd7174f82,0x36b8,0x4aa8,0x80,0x0a,0xe9,0x63,0xab,0x2d,0xfa,0xb9);
+DEFINE_GUID(IID_ICallbackWithNoReentrancyToApplicationSTA, 0x0a299774,0x3e4e,0xfc42,0x1d,0x9d,0x72,0xce,0xe1,0x05,0xca,0x57);
 
 DEFINE_GUID(FMTID_SummaryInformation,0xF29F85E0,0x4FF9,0x1068,0xAB,0x91,0x08,0x00,0x2B,0x27,0xB3,0xD9);
 DEFINE_GUID(FMTID_DocSummaryInformation,0xD5CDD502,0x2E9C,0x101B,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE);
diff --git a/libs/vkd3d/AUTHORS b/libs/vkd3d/AUTHORS
index a42d303410e3..d78543efc82c 100644
--- a/libs/vkd3d/AUTHORS
+++ b/libs/vkd3d/AUTHORS
@@ -24,6 +24,7 @@ Giovanni Mascellani
 Hans-Kristian Arntzen
 Henri Verbeet
 Isabella Bosia
+Ivan Lyugaev
 Jacek Caban
 Jactry Zeng
 Jan Sikorski
diff --git a/libs/vkd3d/config.h b/libs/vkd3d/config.h
index 0d6b8b145367..ecd16decd98a 100644
--- a/libs/vkd3d/config.h
+++ b/libs/vkd3d/config.h
@@ -1,5 +1,6 @@
 #define PACKAGE_NAME "vkd3d"
-#define PACKAGE_STRING "vkd3d 1.19"
-#define PACKAGE_VERSION "1.19"
+#define PACKAGE_STRING "vkd3d 2.0"
+#define PACKAGE_VERSION "2.0"
 #define PATH_MAX 1024
 #define SONAME_LIBVULKAN "vulkan-1.dll"
+#define HAVE__STRTOF_L 1
diff --git a/libs/vkd3d/include/private/vkd3d_common.h b/libs/vkd3d/include/private/vkd3d_common.h
index 196ab307d0cd..4fdc3fcb8025 100644
--- a/libs/vkd3d/include/private/vkd3d_common.h
+++ b/libs/vkd3d/include/private/vkd3d_common.h
@@ -43,7 +43,7 @@
 #include <intrin.h>
 #endif
 
-#define VKD3D_SHADER_API_VERSION_CURRENT VKD3D_SHADER_API_VERSION_1_19
+#define VKD3D_SHADER_API_VERSION_CURRENT VKD3D_SHADER_API_VERSION_2_0
 
 #ifndef ARRAY_SIZE
 # define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
@@ -129,35 +129,54 @@ static inline uint64_t align(uint64_t addr, size_t alignment)
 
 #ifdef VKD3D_NO_TRACE_MESSAGES
 #define TRACE(args...) do { } while (0)
+#define TRACE_(ch, args...) do { } while (0)
 #define TRACE_ON() (false)
+#define TRACE_ON_(ch) (false)
+#define TRACE_TEXT(text, size) do { } while (0)
+#define TRACE_TEXT_(ch, text, size) do { } while (0)
 #endif
 
 #ifdef VKD3D_NO_DEBUG_MESSAGES
 #define WARN(args...) do { } while (0)
+#define WARN_(ch, args...) do { } while (0)
 #define FIXME(args...) do { } while (0)
+#define FIXME_(ch, args...) do { } while (0)
 #define WARN_ON() (false)
+#define WARN_ON_(ch) (false)
 #define FIXME_ONCE(args...) do { } while (0)
+#define FIXME_ONCE_(ch, args...) do { } while (0)
 #endif
 
 #ifdef VKD3D_NO_ERROR_MESSAGES
 #define ERR(args...) do { } while (0)
+#define ERR_(ch, args...) do { } while (0)
 #define MESSAGE(args...) do { } while (0)
+#define MESSAGE_(ch, args...) do { } while (0)
 #endif
 
-enum vkd3d_dbg_level
+enum vkd3d_debug_class
 {
-    VKD3D_DBG_LEVEL_NONE,
-    VKD3D_DBG_LEVEL_MESSAGE,
-    VKD3D_DBG_LEVEL_ERR,
-    VKD3D_DBG_LEVEL_FIXME,
-    VKD3D_DBG_LEVEL_WARN,
-    VKD3D_DBG_LEVEL_TRACE,
+    VKD3D_DEBUG_CLASS_MESSAGE,
+    VKD3D_DEBUG_CLASS_ERR,
+    VKD3D_DEBUG_CLASS_FIXME,
+    VKD3D_DEBUG_CLASS_WARN,
+    VKD3D_DEBUG_CLASS_TRACE,
+
+    VKD3D_DEBUG_CLASS_INIT = 7  /* lazy init flag */
+};
+
+struct vkd3d_debug_channel
+{
+    uint8_t flags;
+    char name[15];
 };
 
-enum vkd3d_dbg_level vkd3d_dbg_get_level(const char *vkd3d_dbg_env_name);
+uint8_t vkd3d_debug_channel_get_flags(struct vkd3d_debug_channel *channel, const char *vkd3d_dbg_env_name);
+void vkd3d_debug_channel_print_text(struct vkd3d_debug_channel *channel, const char *vkd3d_dbg_env_name,
+        enum vkd3d_debug_class cls, const char *function, const char *text, size_t size);
+void vkd3d_debug_channel_printf(struct vkd3d_debug_channel *channel, const char *vkd3d_dbg_env_name,
+        enum vkd3d_debug_class cls, const char *function, const char *fmt, ...) VKD3D_PRINTF_FUNC(5, 6);
 
-void vkd3d_dbg_printf(const char *vkd3d_dbg_env_name, enum vkd3d_dbg_level level,
-        const char *function, const char *fmt, ...) VKD3D_PRINTF_FUNC(4, 5);
 void vkd3d_dbg_set_log_callback(PFN_vkd3d_log callback);
 
 const char *vkd3d_dbg_sprintf(const char *fmt, ...) VKD3D_PRINTF_FUNC(1, 2);
@@ -166,21 +185,25 @@ const char *debugstr_a(const char *str);
 const char *debugstr_an(const char *str, size_t n);
 const char *debugstr_w(const WCHAR *wstr, size_t wchar_size);
 
-#define VKD3D_DBG_LOG(level) \
+#define VKD3D_DBG_LOG(cls, ch) \
         do { \
-        const enum vkd3d_dbg_level vkd3d_dbg_level = VKD3D_DBG_LEVEL_##level; \
-        VKD3D_DBG_PRINTF_##level
+            const enum vkd3d_debug_class vkd3d_dbg_class = VKD3D_DEBUG_CLASS_##cls; \
+            struct vkd3d_debug_channel *vkd3d_dbg_channel = (ch); \
+            VKD3D_DBG_PRINTF_##cls
 
-#define VKD3D_DBG_LOG_ONCE(first_time_level, level) \
+#define VKD3D_DBG_LOG_ONCE(first_time_cls, cls, ch) \
         do { \
-        static bool vkd3d_dbg_next_time; \
-        const enum vkd3d_dbg_level vkd3d_dbg_level = vkd3d_dbg_next_time \
-        ? VKD3D_DBG_LEVEL_##level : VKD3D_DBG_LEVEL_##first_time_level; \
-        vkd3d_dbg_next_time = true; \
-        VKD3D_DBG_PRINTF_##level
+            static bool vkd3d_dbg_next_time; \
+            const enum vkd3d_debug_class vkd3d_dbg_class = vkd3d_dbg_next_time \
+                    ? VKD3D_DEBUG_CLASS_##cls : VKD3D_DEBUG_CLASS_##first_time_cls; \
+            struct vkd3d_debug_channel *vkd3d_dbg_channel = (ch); \
+            vkd3d_dbg_next_time = true; \
+            VKD3D_DBG_PRINTF_##cls
 
 #define VKD3D_DBG_PRINTF(...) \
-        vkd3d_dbg_printf(VKD3D_DEBUG_ENV_NAME, vkd3d_dbg_level, __FUNCTION__, __VA_ARGS__); } while (0)
+            vkd3d_debug_channel_printf(vkd3d_dbg_channel, VKD3D_DEBUG_ENV_NAME, \
+                    vkd3d_dbg_class, __FUNCTION__, __VA_ARGS__); \
+        } while (0)
 
 #define VKD3D_DBG_PRINTF_TRACE(...) VKD3D_DBG_PRINTF(__VA_ARGS__)
 #define VKD3D_DBG_PRINTF_WARN(...) VKD3D_DBG_PRINTF(__VA_ARGS__)
@@ -189,8 +212,9 @@ const char *debugstr_w(const WCHAR *wstr, size_t wchar_size);
 
 #ifdef VKD3D_ABORT_ON_ERR
 #define VKD3D_DBG_PRINTF_ERR(...) \
-        vkd3d_dbg_printf(VKD3D_DEBUG_ENV_NAME, vkd3d_dbg_level, __FUNCTION__, __VA_ARGS__); \
-        abort(); \
+            vkd3d_debug_channel_printf(vkd3d_dbg_channel, VKD3D_DEBUG_ENV_NAME, \
+                    vkd3d_dbg_class, __FUNCTION__, __VA_ARGS__); \
+            abort(); \
         } while (0)
 #else
 #define VKD3D_DBG_PRINTF_ERR(...) VKD3D_DBG_PRINTF(__VA_ARGS__)
@@ -203,37 +227,65 @@ const char *debugstr_w(const WCHAR *wstr, size_t wchar_size);
 #endif
 
 #ifndef TRACE
-#define TRACE   VKD3D_DBG_LOG(TRACE)
+#define TRACE        VKD3D_DBG_LOG(TRACE, vkd3d_debug_channel_default)
+#define TRACE_(ch)   VKD3D_DBG_LOG(TRACE, &vkd3d_debug_channel__##ch)
+#define TRACE_TEXT(text, size) \
+        vkd3d_debug_channel_print_text(vkd3d_debug_channel_default, \
+                VKD3D_DEBUG_ENV_NAME, VKD3D_DEBUG_CLASS_TRACE, __FUNCTION__, text, size)
+#define TRACE_TEXT_(ch, text, size) \
+        vkd3d_debug_channel_print_text(&vkd3d_debug_channel__##ch, \
+                VKD3D_DEBUG_ENV_NAME, VKD3D_DEBUG_CLASS_TRACE, __FUNCTION__, text, size)
 #endif
 
 #ifndef WARN
-#define WARN    VKD3D_DBG_LOG(WARN)
+#define WARN         VKD3D_DBG_LOG(WARN, vkd3d_debug_channel_default)
+#define WARN_(ch)    VKD3D_DBG_LOG(WARN, &vkd3d_debug_channel__##ch)
 #endif
 
 #ifndef FIXME
-#define FIXME   VKD3D_DBG_LOG(FIXME)
+#define FIXME        VKD3D_DBG_LOG(FIXME, vkd3d_debug_channel_default)
+#define FIXME_(ch)   VKD3D_DBG_LOG(FIXME, &vkd3d_debug_channel__##ch)
 #endif
 
 #ifndef ERR
-#define ERR     VKD3D_DBG_LOG(ERR)
+#define ERR          VKD3D_DBG_LOG(ERR, vkd3d_debug_channel_default)
+#define ERR_(ch)     VKD3D_DBG_LOG(ERR, &vkd3d_debug_channel__##ch)
 #endif
 
 #ifndef MESSAGE
-#define MESSAGE VKD3D_DBG_LOG(MESSAGE)
+#define MESSAGE      VKD3D_DBG_LOG(MESSAGE, vkd3d_debug_channel_default)
+#define MESSAGE_(ch) VKD3D_DBG_LOG(MESSAGE, &vkd3d_debug_channel__##ch)
 #endif
 
 #ifndef TRACE_ON
-#define TRACE_ON() (vkd3d_dbg_get_level(VKD3D_DEBUG_ENV_NAME) == VKD3D_DBG_LEVEL_TRACE)
+#define TRACE_ON()    (vkd3d_debug_channel_get_flags(vkd3d_debug_channel_default, VKD3D_DEBUG_ENV_NAME) \
+        & (1u << VKD3D_DEBUG_CLASS_TRACE))
+#define TRACE_ON_(ch) (vkd3d_debug_channel_get_flags(&vkd3d_debug_channel__##ch, VKD3D_DEBUG_ENV_NAME) \
+        & (1u << VKD3D_DEBUG_CLASS_TRACE))
 #endif
 
 #ifndef WARN_ON
-#define WARN_ON() (vkd3d_dbg_get_level(VKD3D_DEBUG_ENV_NAME) >= VKD3D_DBG_LEVEL_WARN)
+#define WARN_ON()    (vkd3d_debug_channel_get_flags(vkd3d_debug_channel_default, VKD3D_DEBUG_ENV_NAME) \
+        & (1u << VKD3D_DEBUG_CLASS_WARN))
+#define WARN_ON_(ch) (vkd3d_debug_channel_get_flags(&vkd3d_debug_channel__##ch, VKD3D_DEBUG_ENV_NAME) \
+        & (1u << VKD3D_DEBUG_CLASS_WARN))
 #endif
 
 #ifndef FIXME_ONCE
-#define FIXME_ONCE VKD3D_DBG_LOG_ONCE(FIXME, WARN)
+#define FIXME_ONCE      VKD3D_DBG_LOG_ONCE(FIXME, WARN, vkd3d_debug_channel_default)
+#define FIXME_ONCE_(ch) VKD3D_DBG_LOG_ONCE(FIXME, WARN, &vkd3d_debug_channel__##ch)
 #endif
 
+#define VKD3D_DECLARE_DEBUG_CHANNEL(ch) \
+        static struct vkd3d_debug_channel vkd3d_debug_channel__##ch = {0xffu, #ch}; \
+        STATIC_ASSERT(sizeof(#ch) <= sizeof(vkd3d_debug_channel__##ch.name));
+#define VKD3D_DEFAULT_DEBUG_CHANNEL(ch) \
+        static struct vkd3d_debug_channel vkd3d_debug_channel__##ch = {0xffu, #ch}; \
+        STATIC_ASSERT(sizeof(#ch) <= sizeof(vkd3d_debug_channel__##ch.name)); \
+        static struct vkd3d_debug_channel * const vkd3d_debug_channel_default = &vkd3d_debug_channel__##ch;
+
+VKD3D_DEFAULT_DEBUG_CHANNEL(vkd3d)
+
 static inline const char *debugstr_guid(const GUID *guid)
 {
     if (!guid)
@@ -400,6 +452,48 @@ static inline bool vkd3d_object_range_overflow(size_t start, size_t count, size_
     return (~(size_t)0 - start) / size < count;
 }
 
+/* Based on the implementation in the OpenGL Mathematics library. */
+static inline uint32_t vkd3d_f32_from_f16(uint16_t value)
+{
+    uint32_t s = (value & 0x8000u) << 16;
+    uint32_t e = (value >> 10) & 0x1fu;
+    uint32_t m = value & 0x3ffu;
+
+    if (!e)
+    {
+        if (!m)
+        {
+            /* Plus or minus zero. */
+            return s;
+        }
+        else
+        {
+            /* Denormalised number; renormalise it. */
+            while (!(m & 0x400u))
+            {
+                m <<= 1;
+                --e;
+            }
+
+            ++e;
+            m &= ~0x400u;
+        }
+    }
+    else if (e == 31u)
+    {
+        /* Positive or negative infinity for zero 'm'.
+         * NaN for non-zero 'm'; preserve sign and significand bits. */
+        return s | 0x7f800000u | (m << 13);
+    }
+
+    /* Normalised number. */
+    e += 127u - 15u;
+    m <<= 13;
+
+    /* Assemble s, e and m. */
+    return s | (e << 23) | m;
+}
+
 static inline uint16_t vkd3d_make_u16(uint8_t low, uint8_t high)
 {
     return low | ((uint16_t)high << 8);
@@ -420,6 +514,11 @@ static inline int vkd3d_u64_compare(uint64_t x, uint64_t y)
     return (x > y) - (x < y);
 }
 
+static inline int vkd3d_ptr_compare(const void *x, const void *y)
+{
+    return (x > y) - (x < y);
+}
+
 #define VKD3D_BITMAP_SIZE(x) (((x) + 0x1f) >> 5)
 
 static inline bool bitmap_clear(uint32_t *map, unsigned int idx)
diff --git a/libs/vkd3d/include/private/vkd3d_shader_utils.h b/libs/vkd3d/include/private/vkd3d_shader_utils.h
index 465734dfbffc..7436f0ddebf5 100644
--- a/libs/vkd3d/include/private/vkd3d_shader_utils.h
+++ b/libs/vkd3d/include/private/vkd3d_shader_utils.h
@@ -20,6 +20,14 @@
 #define __VKD3D_SHADER_UTILS_H
 
 #include "vkd3d_shader.h"
+#include <sys/stat.h>
+
+/* S_ISREG may not be defined when building for Windows. MinGW provides a more
+ * POSIX-like environment that does define S_ISREG, but the Wine/msvcrt
+ * headers do not. */
+#ifndef S_ISREG
+# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
 
 static inline enum vkd3d_result vkd3d_shader_parse_dxbc_source_type(const struct vkd3d_shader_code *dxbc,
         enum vkd3d_shader_source_type *type, char **messages)
@@ -56,4 +64,52 @@ static inline enum vkd3d_result vkd3d_shader_parse_dxbc_source_type(const struct
     return VKD3D_OK;
 }
 
+static inline enum vkd3d_result vkd3d_shader_code_from_file(struct vkd3d_shader_code *shader, FILE *f)
+{
+    size_t size = 4096;
+    struct stat st;
+    size_t pos = 0;
+    uint8_t *data;
+    size_t ret;
+
+    memset(shader, 0, sizeof(*shader));
+
+    if (fstat(fileno(f), &st) == -1)
+        return VKD3D_ERROR;
+
+    if (S_ISREG(st.st_mode))
+        size = st.st_size;
+
+    if (!(data = malloc(size)))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+
+    for (;;)
+    {
+        if (pos >= size)
+        {
+            if (size > SIZE_MAX / 2 || !(data = realloc(data, size * 2)))
+            {
+                free(data);
+                return VKD3D_ERROR_OUT_OF_MEMORY;
+            }
+            size *= 2;
+        }
+
+        if (!(ret = fread(&data[pos], 1, size - pos, f)))
+            break;
+        pos += ret;
+    }
+
+    if (!feof(f))
+    {
+        free(data);
+        return VKD3D_ERROR;
+    }
+
+    shader->code = data;
+    shader->size = pos;
+
+    return VKD3D_OK;
+}
+
 #endif  /* __VKD3D_SHADER_UTILS_H */
diff --git a/libs/vkd3d/include/vkd3d.h b/libs/vkd3d/include/vkd3d.h
index da4362c1e6fa..b14752165a29 100644
--- a/libs/vkd3d/include/vkd3d.h
+++ b/libs/vkd3d/include/vkd3d.h
@@ -104,6 +104,7 @@ enum vkd3d_api_version
     VKD3D_API_VERSION_1_17,
     VKD3D_API_VERSION_1_18,
     VKD3D_API_VERSION_1_19,
+    VKD3D_API_VERSION_2_0,
 
     VKD3D_FORCE_32_BIT_ENUM(VKD3D_API_VERSION),
 };
diff --git a/libs/vkd3d/include/vkd3d_shader.h b/libs/vkd3d/include/vkd3d_shader.h
index dd2a8a8afeea..084bda762c0b 100644
--- a/libs/vkd3d/include/vkd3d_shader.h
+++ b/libs/vkd3d/include/vkd3d_shader.h
@@ -62,6 +62,7 @@ enum vkd3d_shader_api_version
     VKD3D_SHADER_API_VERSION_1_17,
     VKD3D_SHADER_API_VERSION_1_18,
     VKD3D_SHADER_API_VERSION_1_19,
+    VKD3D_SHADER_API_VERSION_2_0,
 
     VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_API_VERSION),
 };
@@ -132,6 +133,11 @@ enum vkd3d_shader_structure_type
      * \since 1.18
      */
     VKD3D_SHADER_STRUCTURE_TYPE_D3DBC_SOURCE_INFO,
+    /**
+     * The structure is a vkd3d_shader_scan_denormal_mode_info structure.
+     * \since 2.0
+     */
+    VKD3D_SHADER_STRUCTURE_TYPE_SCAN_DENORMAL_MODE_INFO,
 
     VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_STRUCTURE_TYPE),
 };
@@ -208,7 +214,9 @@ enum vkd3d_shader_compile_option_backward_compatibility
      *  - POSITION to SV_Position for vertex shader outputs, pixel shader inputs,
      *    and geometry shader inputs and outputs;
      *  - COLORN to SV_TargetN for pixel shader outputs;
-     *  - DEPTH to SV_Depth for pixel shader outputs.
+     *  - DEPTH to SV_Depth for pixel shader outputs;
+     *  - VFACE to SV_IsFrontFace for pixel shader inputs;
+     *  - VPOS to SV_Position for pixel shader inputs.
      */
     VKD3D_SHADER_COMPILE_OPTION_BACKCOMPAT_MAP_SEMANTIC_NAMES = 0x00000001,
     /**
@@ -222,6 +230,16 @@ enum vkd3d_shader_compile_option_backward_compatibility
      *  \since 1.14
      */
     VKD3D_SHADER_COMPILE_OPTION_DOUBLE_AS_FLOAT_ALIAS = 0x00000002,
+    /**
+     *  Causes all uniform variables in global scope to be const.
+     *  This includes variables declared without either 'uniform' or 'static',
+     *  but does not include uniforms declared as arguments to the entry point.
+     *
+     *  This option is disabled by default.
+     *
+     *  \since 2.0
+     */
+    VKD3D_SHADER_COMPILE_OPTION_CONST_GLOBAL_UNIFORMS = 0x00000004,
 
     VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_COMPILE_OPTION_BACKWARD_COMPATIBILITY),
 };
@@ -382,6 +400,27 @@ enum vkd3d_shader_compile_option_name
      * \since 1.12
      */
     VKD3D_SHADER_COMPILE_OPTION_INCLUDE_EMPTY_BUFFERS_IN_EFFECTS = 0x0000000d,
+    /**
+     * Override the denormal mode for f16 (half) numbers. \a value is a member
+     * of enum vkd3d_shader_denormal_mode.
+     *
+     * \since 2.0
+     */
+    VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F16 = 0x0000000e,
+    /**
+     * Override the denormal mode for f32 (float) numbers. \a value is a member
+     * of enum vkd3d_shader_denormal_mode.
+     *
+     * \since 2.0
+     */
+    VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F32 = 0x0000000f,
+    /**
+     * Override the denormal mode for f64 (double) numbers. \a value is a member
+     * of enum vkd3d_shader_denormal_mode.
+     *
+     * \since 2.0
+     */
+    VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F64 = 0x00000010,
 
     VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_COMPILE_OPTION_NAME),
 };
@@ -1689,6 +1728,8 @@ enum vkd3d_shader_spirv_extension
     VKD3D_SHADER_SPIRV_EXTENSION_EXT_VIEWPORT_INDEX_LAYER,
     /** \since 1.12 */
     VKD3D_SHADER_SPIRV_EXTENSION_EXT_FRAGMENT_SHADER_INTERLOCK,
+    /** \since 2.0 */
+    VKD3D_SHADER_SPIRV_EXTENSION_KHR_FLOAT_CONTROLS,
 
     VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_SPIRV_EXTENSION),
 };
@@ -2415,6 +2456,46 @@ struct vkd3d_shader_scan_thread_group_size_info
     unsigned int x, y, z;
 };
 
+/**
+ * Specifies how denormal floating-point numbers should be treated.
+ *
+ * \since 2.0
+ */
+enum vkd3d_shader_denormal_mode
+{
+    /** No particular denormal mode is requested. */
+    VKD3D_SHADER_DENORMAL_MODE_ANY = 0,
+    /** Denormal values should be preserved. */
+    VKD3D_SHADER_DENORMAL_MODE_PRESERVE = 1,
+    /** Denormal values should be flushed to zero. */
+    VKD3D_SHADER_DENORMAL_MODE_FLUSH_TO_ZERO = 2,
+
+    VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_DENORMAL_MODE),
+};
+
+/**
+ * A chained structure describing how a shader expects denormal floating-point
+ * values to be handled.
+ *
+ * This structure extends vkd3d_shader_compile_info.
+ *
+ * \since 2.0
+ */
+struct vkd3d_shader_scan_denormal_mode_info
+{
+    /** Must be set to VKD3D_SHADER_STRUCTURE_TYPE_SCAN_DENORMAL_MODE_INFO. */
+    enum vkd3d_shader_structure_type type;
+    /** Optional pointer to a structure containing further parameters. */
+    const void *next;
+
+    /** The denormal mode for f16 (half) numbers. */
+    enum vkd3d_shader_denormal_mode f16_denormal_mode;
+    /** The denormal mode for f32 (float) numbers. */
+    enum vkd3d_shader_denormal_mode f32_denormal_mode;
+    /** The denormal mode for f64 (double) numbers. */
+    enum vkd3d_shader_denormal_mode f64_denormal_mode;
+};
+
 /**
  * A chained structure containing legacy Direct3D bytecode compilation parameters.
  * This structure specifies some information about the source environment that
@@ -2970,6 +3051,7 @@ VKD3D_SHADER_API const enum vkd3d_shader_target_type *vkd3d_shader_get_supported
  * - vkd3d_shader_parameter_info
  * - vkd3d_shader_preprocess_info
  * - vkd3d_shader_scan_combined_resource_sampler_info
+ * - vkd3d_shader_scan_denormal_mode_info
  * - vkd3d_shader_scan_descriptor_info
  * - vkd3d_shader_scan_hull_shader_tessellation_info
  * - vkd3d_shader_scan_signature_info
@@ -3169,6 +3251,7 @@ VKD3D_SHADER_API int vkd3d_shader_convert_root_signature(struct vkd3d_shader_ver
  * - vkd3d_shader_parameter_info
  * - vkd3d_shader_preprocess_info
  * - vkd3d_shader_scan_combined_resource_sampler_info
+ * - vkd3d_shader_scan_denormal_mode_info
  * - vkd3d_shader_scan_descriptor_info
  * - vkd3d_shader_scan_hull_shader_tessellation_info
  * - vkd3d_shader_scan_signature_info
diff --git a/libs/vkd3d/libs/vkd3d-common/debug.c b/libs/vkd3d/libs/vkd3d-common/debug.c
index b5d74ca3dc7a..f83f70cd55fa 100644
--- a/libs/vkd3d/libs/vkd3d-common/debug.c
+++ b/libs/vkd3d/libs/vkd3d-common/debug.c
@@ -40,40 +40,124 @@
 #define VKD3D_DEBUG_BUFFER_COUNT 64
 #define VKD3D_DEBUG_BUFFER_SIZE 512
 
-static const char *const debug_level_names[] =
+static const uint8_t VKD3D_DEBUG_DEFAULT_FLAGS = (1u << VKD3D_DEBUG_CLASS_MESSAGE)
+        | (1u << VKD3D_DEBUG_CLASS_ERR) | (1u << VKD3D_DEBUG_CLASS_FIXME);
+
+static const char *const debug_class_names[] =
 {
-    [VKD3D_DBG_LEVEL_NONE ] =   "none",
-    [VKD3D_DBG_LEVEL_MESSAGE] = "message",
-    [VKD3D_DBG_LEVEL_ERR  ] =   "err",
-    [VKD3D_DBG_LEVEL_FIXME] =   "fixme",
-    [VKD3D_DBG_LEVEL_WARN ] =   "warn",
-    [VKD3D_DBG_LEVEL_TRACE] =   "trace",
+    [VKD3D_DEBUG_CLASS_MESSAGE] = "message",
+    [VKD3D_DEBUG_CLASS_ERR    ] = "err",
+    [VKD3D_DEBUG_CLASS_FIXME  ] = "fixme",
+    [VKD3D_DEBUG_CLASS_WARN   ] = "warn",
+    [VKD3D_DEBUG_CLASS_TRACE  ] = "trace",
 };
 
-enum vkd3d_dbg_level vkd3d_dbg_get_level(const char *vkd3d_dbg_env_name)
+static uint8_t vkd3d_debug_channel_parse_flags(struct vkd3d_debug_channel *channel, const char *str)
 {
-    static unsigned int level = ~0u;
-    const char *vkd3d_debug;
+    uint8_t flags, default_flags = VKD3D_DEBUG_DEFAULT_FLAGS;
+    bool channel_specifics = false;
+    char *opt, *next, *options;
+    const char *p, *name;
     unsigned int i;
+    size_t len;
 
-    if (level != ~0u)
-        return level;
-
-    if (!(vkd3d_debug = getenv(vkd3d_dbg_env_name)))
-        vkd3d_debug = "";
+    if (!(options = vkd3d_strdup(str)))
+        return default_flags;
 
-    for (i = 0; i < ARRAY_SIZE(debug_level_names); ++i)
+    for (opt = options; opt; opt = next)
     {
-        if (!strcmp(debug_level_names[i], vkd3d_debug))
+        uint8_t set = 0, clear = 0;
+
+        if ((next = strchr(opt, ',')))
+            *next++ = 0;
+
+        p = opt + strcspn(opt, "+-");
+        if (!*p)
+            p = opt; /* Assume it's a debug channel name. */
+
+        if (p > opt)
+        {
+            for (i = 0; i < ARRAY_SIZE(debug_class_names); ++i)
+            {
+                if (!(name = debug_class_names[i]))
+                    continue;
+                if ((len = strlen(name)) != (p - opt))
+                    continue;
+                if (memcmp(opt, name, len))
+                    continue;
+
+                if (*p == '+')
+                    set |= 1u << i;
+                else
+                    clear |= 1u << i;
+                break;
+            }
+            if (i == ARRAY_SIZE(debug_class_names))
+                continue; /* Bad class name, skip it. */
+        }
+        else
         {
-            level = i;
-            return level;
+            if (*p == '-')
+                clear = ~0;
+            else
+                set = ~0;
+        }
+        if (*p == '+' || *p == '-')
+            ++p;
+        if (!*p)
+            continue;
+
+        if (!strcmp(p, "all"))
+        {
+            default_flags = (default_flags & ~clear) | set;
+        }
+        else if(!strcmp(p, channel->name))
+        {
+            if (!channel_specifics)
+                flags = default_flags;
+            flags = (flags & ~clear) | set;
+            channel_specifics = true;
         }
     }
 
-    /* Default debug level. */
-    level = VKD3D_DBG_LEVEL_FIXME;
-    return level;
+    vkd3d_free(options);
+
+    if (!channel_specifics)
+        flags = default_flags;
+
+    return flags;
+}
+
+static void vkd3d_debug_channel_init_flags(struct vkd3d_debug_channel *channel, const char *vkd3d_dbg_env_name)
+{
+    const char *vkd3d_debug;
+
+    if (!(vkd3d_debug = getenv(vkd3d_dbg_env_name)))
+    {
+        channel->flags = VKD3D_DEBUG_DEFAULT_FLAGS;
+        return;
+    }
+
+    if (!strcmp(vkd3d_debug, "trace"))
+        vkd3d_debug = "+all";
+    else if (!strcmp(vkd3d_debug, "warn"))
+        vkd3d_debug = "warn+all";
+    else if (!strcmp(vkd3d_debug, "fixme"))
+        vkd3d_debug = "";
+    else if (!strcmp(vkd3d_debug, "err"))
+        vkd3d_debug = "fixme-all";
+    else if (!strcmp(vkd3d_debug, "none"))
+        vkd3d_debug = "-all";
+
+    channel->flags = vkd3d_debug_channel_parse_flags(channel, vkd3d_debug);
+}
+
+uint8_t vkd3d_debug_channel_get_flags(struct vkd3d_debug_channel *channel, const char *vkd3d_dbg_env_name)
+{
+    if (channel->flags & (1u << VKD3D_DEBUG_CLASS_INIT))
+        vkd3d_debug_channel_init_flags(channel, vkd3d_dbg_env_name);
+
+    return channel->flags;
 }
 
 static PFN_vkd3d_log log_callback;
@@ -106,28 +190,47 @@ static uint64_t get_pthread_threadid(void)
 }
 #endif
 
-void vkd3d_dbg_printf(const char *vkd3d_dbg_env_name,
-        enum vkd3d_dbg_level level, const char *function, const char *fmt, ...)
+void vkd3d_debug_channel_printf(struct vkd3d_debug_channel *channel, const char *vkd3d_dbg_env_name,
+        enum vkd3d_debug_class cls, const char *function, const char *fmt, ...)
 {
+    const char *cls_name = debug_class_names[cls];
     va_list args;
 
-    if (vkd3d_dbg_get_level(vkd3d_dbg_env_name) < level)
+    if (!(vkd3d_debug_channel_get_flags(channel, vkd3d_dbg_env_name) & (1u << cls)))
         return;
 
 #ifdef _WIN32
-    vkd3d_dbg_output("vkd3d:%04lx:%s:%s ", GetCurrentThreadId(), debug_level_names[level], function);
+    vkd3d_dbg_output("vkd3d:%04lx:%s:%s:%s ", GetCurrentThreadId(), cls_name, channel->name, function);
 #elif HAVE_GETTID
-    vkd3d_dbg_output("vkd3d:%u:%s:%s ", gettid(), debug_level_names[level], function);
+    vkd3d_dbg_output("vkd3d:%u:%s:%s:%s ", gettid(), cls_name, channel->name, function);
 #elif HAVE_PTHREAD_THREADID_NP
-    vkd3d_dbg_output("vkd3d:%"PRIu64":%s:%s ", get_pthread_threadid(), debug_level_names[level], function);
+    vkd3d_dbg_output("vkd3d:%"PRIu64":%s:%s:%s ", get_pthread_threadid(), cls_name, channel->name, function);
 #else
-    vkd3d_dbg_output("vkd3d:%s:%s ", debug_level_names[level], function);
+    vkd3d_dbg_output("vkd3d:%s:%s:%s ", cls_name, channel->name, function);
 #endif
     va_start(args, fmt);
     vkd3d_dbg_voutput(fmt, args);
     va_end(args);
 }
 
+void vkd3d_debug_channel_print_text(struct vkd3d_debug_channel *channel, const char *vkd3d_dbg_env_name,
+        enum vkd3d_debug_class cls, const char *function, const char *text, size_t size)
+{
+    const char *p, *q, *end = text + size;
+
+    if (!(vkd3d_debug_channel_get_flags(channel, vkd3d_dbg_env_name) & (1u << cls)))
+        return;
+
+    for (p = text; p < end; p = q)
+    {
+        if (!(q = memchr(p, '\n', end - p)))
+            q = end;
+        else
+            ++q;
+        vkd3d_debug_channel_printf(channel, vkd3d_dbg_env_name, cls, function, "%.*s", (int)(q - p), p);
+    }
+}
+
 void vkd3d_dbg_set_log_callback(PFN_vkd3d_log callback)
 {
     log_callback = callback;
diff --git a/libs/vkd3d/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d/libs/vkd3d-shader/d3d_asm.c
index f06870718ece..184bcd629482 100644
--- a/libs/vkd3d/libs/vkd3d-shader/d3d_asm.c
+++ b/libs/vkd3d/libs/vkd3d-shader/d3d_asm.c
@@ -27,76 +27,20 @@
 #include <stdio.h>
 #include <math.h>
 
-static const char * const shader_register_names[] =
-{
-    [VKD3DSPR_ADDR              ] = "a",
-    [VKD3DSPR_ATTROUT           ] = "oD",
-    [VKD3DSPR_COLOROUT          ] = "oC",
-    [VKD3DSPR_COMBINED_SAMPLER  ] = "s",
-    [VKD3DSPR_CONST             ] = "c",
-    [VKD3DSPR_CONSTBOOL         ] = "b",
-    [VKD3DSPR_CONSTBUFFER       ] = "cb",
-    [VKD3DSPR_CONSTINT          ] = "i",
-    [VKD3DSPR_COVERAGE          ] = "vCoverage",
-    [VKD3DSPR_DEPTHOUT          ] = "oDepth",
-    [VKD3DSPR_DEPTHOUTGE        ] = "oDepthGE",
-    [VKD3DSPR_DEPTHOUTLE        ] = "oDepthLE",
-    [VKD3DSPR_FORKINSTID        ] = "vForkInstanceId",
-    [VKD3DSPR_FUNCTIONBODY      ] = "fb",
-    [VKD3DSPR_FUNCTIONPOINTER   ] = "fp",
-    [VKD3DSPR_GROUPSHAREDMEM    ] = "g",
-    [VKD3DSPR_GSINSTID          ] = "vGSInstanceID",
-    [VKD3DSPR_IDXTEMP           ] = "x",
-    [VKD3DSPR_IMMCONST          ] = "l",
-    [VKD3DSPR_IMMCONST64        ] = "d",
-    [VKD3DSPR_IMMCONSTBUFFER    ] = "icb",
-    [VKD3DSPR_INCONTROLPOINT    ] = "vicp",
-    [VKD3DSPR_INPUT             ] = "v",
-    [VKD3DSPR_JOININSTID        ] = "vJoinInstanceId",
-    [VKD3DSPR_LABEL             ] = "l",
-    [VKD3DSPR_LOCALTHREADID     ] = "vThreadIDInGroup",
-    [VKD3DSPR_LOCALTHREADINDEX  ] = "vThreadIDInGroupFlattened",
-    [VKD3DSPR_LOOP              ] = "aL",
-    [VKD3DSPR_NULL              ] = "null",
-    [VKD3DSPR_OUTCONTROLPOINT   ] = "vocp",
-    [VKD3DSPR_OUTPOINTID        ] = "vOutputControlPointID",
-    [VKD3DSPR_OUTPUT            ] = "o",
-    [VKD3DSPR_OUTSTENCILREF     ] = "oStencilRef",
-    [VKD3DSPR_PARAMETER         ] = "parameter",
-    [VKD3DSPR_PATCHCONST        ] = "vpc",
-    [VKD3DSPR_POINT_COORD       ] = "vPointCoord",
-    [VKD3DSPR_PREDICATE         ] = "p",
-    [VKD3DSPR_PRIMID            ] = "primID",
-    [VKD3DSPR_RASTERIZER        ] = "rasterizer",
-    [VKD3DSPR_RESOURCE          ] = "t",
-    [VKD3DSPR_SAMPLEMASK        ] = "oMask",
-    [VKD3DSPR_SAMPLER           ] = "s",
-    [VKD3DSPR_SSA               ] = "sr",
-    [VKD3DSPR_STREAM            ] = "m",
-    [VKD3DSPR_TEMP              ] = "r",
-    [VKD3DSPR_TESSCOORD         ] = "vDomainLocation",
-    [VKD3DSPR_TEXCRDOUT         ] = "oT",
-    [VKD3DSPR_TEXTURE           ] = "t",
-    [VKD3DSPR_THREADGROUPID     ] = "vThreadGroupID",
-    [VKD3DSPR_THREADID          ] = "vThreadID",
-    [VKD3DSPR_UAV               ] = "u",
-    [VKD3DSPR_UNDEF             ] = "undef",
-    [VKD3DSPR_WAVELANECOUNT     ] = "vWaveLaneCount",
-    [VKD3DSPR_WAVELANEINDEX     ] = "vWaveLaneIndex",
-};
-
 struct vkd3d_d3d_asm_colours
 {
     const char *reset;
+    const char *enumerant;
     const char *error;
+    const char *label;
     const char *literal;
     const char *modifier;
     const char *opcode;
     const char *reg;
     const char *swizzle;
+    const char *type;
     const char *version;
     const char *write_mask;
-    const char *label;
 };
 
 struct vkd3d_d3d_asm_compiler
@@ -120,19 +64,31 @@ static void shader_dump_global_flags(struct vkd3d_d3d_asm_compiler *compiler, en
     global_flag_info[] =
     {
         {VKD3DSGF_REFACTORING_ALLOWED,               "refactoringAllowed"},
+        {VKD3DSGF_ENABLE_DOUBLE_PRECISION_FLOAT_OPS, "enableDoublePrecisionFloatOps"},
         {VKD3DSGF_FORCE_EARLY_DEPTH_STENCIL,         "forceEarlyDepthStencil"},
         {VKD3DSGF_ENABLE_RAW_AND_STRUCTURED_BUFFERS, "enableRawAndStructuredBuffers"},
-        {VKD3DSGF_ENABLE_MINIMUM_PRECISION,          "enableMinimumPrecision"},
         {VKD3DSGF_SKIP_OPTIMIZATION,                 "skipOptimization"},
-        {VKD3DSGF_ENABLE_DOUBLE_PRECISION_FLOAT_OPS, "enableDoublePrecisionFloatOps"},
+        {VKD3DSGF_ENABLE_MINIMUM_PRECISION,          "enableMinimumPrecision"},
         {VKD3DSGF_ENABLE_11_1_DOUBLE_EXTENSIONS,     "enable11_1DoubleExtensions"},
+        {VKD3DSGF_ENABLE_11_1_SHADER_EXTENSIONS,     "enable11_1ShaderExtensions"},
+        {VKD3DSGF_BIND_FOR_DURATION,                 "allResourcesBound"},
+        {VKD3DSGF_ENABLE_VP_AND_RT_ARRAY_INDEX,      "viewportAndRTArrayIndex"},
+        {VKD3DSGF_ENABLE_STENCIL_REF,                "stencilRef"},
+        {VKD3DSGF_ENABLE_RELAXED_TYPED_UAV_FORMATS,  "UAVLoadAdditionalFormats"},
+        {VKD3DSGF_ENABLE_UP_TO_64_UAVS,              "64UAVs"},
+        {VKD3DSGF_ENABLE_UAVS_AT_EVERY_STAGE,        "UAVsAtEveryStage"},
+        {VKD3DSGF_ENABLE_RASTERIZER_ORDERED_VIEWS,   "ROVs"},
+        {VKD3DSGF_ENABLE_WAVE_INTRINSICS,            "waveOps"},
+        {VKD3DSGF_ENABLE_INT64,                      "int64Ops"},
+        {VKD3DSGF_ENABLE_NATIVE_LOW_PRECISION,       "nativeLowPrecision"},
     };
 
     for (i = 0; i < ARRAY_SIZE(global_flag_info); ++i)
     {
         if (global_flags & global_flag_info[i].flag)
         {
-            vkd3d_string_buffer_printf(&compiler->buffer, "%s", global_flag_info[i].name);
+            vkd3d_string_buffer_printf(&compiler->buffer, "%s%s%s",
+                    compiler->colours.enumerant, global_flag_info[i].name, compiler->colours.reset);
             global_flags &= ~global_flag_info[i].flag;
             if (global_flags)
                 vkd3d_string_buffer_printf(&compiler->buffer, " | ");
@@ -140,7 +96,8 @@ static void shader_dump_global_flags(struct vkd3d_d3d_asm_compiler *compiler, en
     }
 
     if (global_flags)
-        vkd3d_string_buffer_printf(&compiler->buffer, "unknown_flags(%#"PRIx64")", (uint64_t)global_flags);
+        vkd3d_string_buffer_printf(&compiler->buffer, "%sunknown_flags(%#"PRIx64")%s",
+                compiler->colours.error, (uint64_t)global_flags, compiler->colours.reset);
 }
 
 static void shader_dump_atomic_op_flags(struct vkd3d_d3d_asm_compiler *compiler, uint32_t atomic_flags)
@@ -429,7 +386,8 @@ static void shader_dump_resource_data_type(struct vkd3d_d3d_asm_compiler *compil
 
         vkd3d_string_buffer_printf(&compiler->buffer, "%s", i == 0 ? "" : ",");
         if (t < ARRAY_SIZE(names) && names[t])
-            vkd3d_string_buffer_printf(&compiler->buffer, "%s", names[t]);
+            vkd3d_string_buffer_printf(&compiler->buffer, "%s%s%s",
+                    compiler->colours.type, names[t], compiler->colours.reset);
         else
             vkd3d_string_buffer_printf(&compiler->buffer, "%s<unhandled data type %#zx>%s",
                     compiler->colours.error, t, compiler->colours.reset);
@@ -483,7 +441,7 @@ static void shader_print_dcl_usage(struct vkd3d_d3d_asm_compiler *compiler,
         }
         if (semantic->resource.reg.reg.type == VKD3DSPR_UAV)
             shader_dump_uav_flags(compiler, flags);
-        vkd3d_string_buffer_printf(buffer, " ");
+        vkd3d_string_buffer_printf(buffer, "%s ", compiler->colours.reset);
         shader_dump_resource_data_type(compiler, semantic->resource_data_type);
         vkd3d_string_buffer_printf(buffer, "%s", suffix);
         return;
@@ -566,6 +524,37 @@ static void shader_print_dcl_usage(struct vkd3d_d3d_asm_compiler *compiler,
 static void shader_print_src_operand(struct vkd3d_d3d_asm_compiler *compiler,
         const char *prefix, const struct vsir_src_operand *src, const char *suffix);
 
+static bool f16_isfinite(uint16_t f)
+{
+    return (f & 0x7c00) != 0x7c00;
+}
+
+static bool f16_signbit(uint16_t f)
+{
+    return f & 0x8000;
+}
+
+static uint16_t f16_negate(uint16_t f)
+{
+    return f ^ 0x8000;
+}
+
+static void shader_print_f16_literal(struct vkd3d_d3d_asm_compiler *compiler,
+        const char *prefix, uint16_t f, const char *suffix)
+{
+    const char *sign = "";
+
+    if (f16_isfinite(f) && f16_signbit(f))
+    {
+        sign = "-";
+        f = f16_negate(f);
+    }
+
+    vkd3d_string_buffer_printf(&compiler->buffer, "%s%s%s", prefix, sign, compiler->colours.literal);
+    vkd3d_string_buffer_print_f16(&compiler->buffer, f);
+    vkd3d_string_buffer_printf(&compiler->buffer, "%s%s", compiler->colours.reset, suffix);
+}
+
 static void shader_print_float_literal(struct vkd3d_d3d_asm_compiler *compiler,
         const char *prefix, float f, const char *suffix)
 {
@@ -598,6 +587,25 @@ static void shader_print_double_literal(struct vkd3d_d3d_asm_compiler *compiler,
     vkd3d_string_buffer_printf(&compiler->buffer, "l%s%s", compiler->colours.reset, suffix);
 }
 
+static void shader_print_i16_literal(struct vkd3d_d3d_asm_compiler *compiler,
+        const char *prefix, int16_t i, const char *suffix)
+{
+    /* Note that we need to handle INT16_MIN here as well. */
+    if (i < 0)
+        vkd3d_string_buffer_printf(&compiler->buffer, "%s-%s%hu%s%s",
+                prefix, compiler->colours.literal, -(uint16_t)i, compiler->colours.reset, suffix);
+    else
+        vkd3d_string_buffer_printf(&compiler->buffer, "%s%s%hd%s%s",
+                prefix, compiler->colours.literal, i, compiler->colours.reset, suffix);
+}
+
+static void shader_print_u16_literal(struct vkd3d_d3d_asm_compiler *compiler,
+        const char *prefix, uint16_t i, const char *suffix)
+{
+    vkd3d_string_buffer_printf(&compiler->buffer, "%s%s%hu%s%s",
+            prefix, compiler->colours.literal, i, compiler->colours.reset, suffix);
+}
+
 static void shader_print_int_literal(struct vkd3d_d3d_asm_compiler *compiler,
         const char *prefix, int i, const char *suffix)
 {
@@ -689,12 +697,13 @@ static void shader_print_subscript_range(struct vkd3d_d3d_asm_compiler *compiler
         vkd3d_string_buffer_printf(&compiler->buffer, "*]");
 }
 
-static void shader_print_register(struct vkd3d_d3d_asm_compiler *compiler, const char *prefix,
-        const struct vkd3d_shader_register *reg, bool is_declaration, const char *suffix)
+static void shader_print_operand(struct vkd3d_d3d_asm_compiler *compiler, const char *prefix,
+        const struct vsir_operand *reg, bool is_declaration, const char *suffix)
 {
     struct vkd3d_string_buffer *buffer = &compiler->buffer;
     unsigned int offset = reg->idx[0].offset;
     bool is_descriptor = false;
+    const char *name;
 
     static const char * const rastout_reg_names[] = {"oPos", "oFog", "oPts"};
     static const char * const misctype_reg_names[] = {"vPos", "vFace"};
@@ -724,8 +733,8 @@ static void shader_print_register(struct vkd3d_d3d_asm_compiler *compiler, const
             /* fall through */
 
         default:
-            if (reg->type < ARRAY_SIZE(shader_register_names) && shader_register_names[reg->type])
-                vkd3d_string_buffer_printf(buffer, "%s", shader_register_names[reg->type]);
+            if ((name = vsir_register_type_get_name(reg->type, NULL)))
+                vkd3d_string_buffer_printf(buffer, "%s", name);
             else
                 vkd3d_string_buffer_printf(buffer, "%s<unhandled register type %#x>%s",
                         compiler->colours.error, reg->type, compiler->colours.reset);
@@ -753,15 +762,27 @@ static void shader_print_register(struct vkd3d_d3d_asm_compiler *compiler, const
             case VSIR_DIMENSION_SCALAR:
                 switch (reg->data_type)
                 {
+                    case VSIR_DATA_BOOL:
+                        shader_print_bool_literal(compiler, "", reg->u.immconst_u32[0], "");
+                        break;
+                    case VSIR_DATA_F16:
+                        shader_print_f16_literal(compiler, "", reg->u.immconst_u32[0], "");
+                        break;
                     case VSIR_DATA_F32:
                         if (untyped)
                             shader_print_untyped_literal(compiler, "", reg->u.immconst_u32[0], "");
                         else
                             shader_print_float_literal(compiler, "", reg->u.immconst_f32[0], "");
                         break;
+                    case VSIR_DATA_I16:
+                        shader_print_i16_literal(compiler, "", reg->u.immconst_u32[0], "");
+                        break;
                     case VSIR_DATA_I32:
                         shader_print_int_literal(compiler, "", reg->u.immconst_u32[0], "");
                         break;
+                    case VSIR_DATA_U16:
+                        shader_print_u16_literal(compiler, "", reg->u.immconst_u32[0], "");
+                        break;
                     case VSIR_DATA_U32:
                         shader_print_uint_literal(compiler, "", reg->u.immconst_u32[0], "");
                         break;
@@ -775,6 +796,18 @@ static void shader_print_register(struct vkd3d_d3d_asm_compiler *compiler, const
             case VSIR_DIMENSION_VEC4:
                 switch (reg->data_type)
                 {
+                    case VSIR_DATA_BOOL:
+                        shader_print_bool_literal(compiler, "", reg->u.immconst_u32[0], "");
+                        shader_print_bool_literal(compiler, ", ", reg->u.immconst_u32[1], "");
+                        shader_print_bool_literal(compiler, ", ", reg->u.immconst_u32[2], "");
+                        shader_print_bool_literal(compiler, ", ", reg->u.immconst_u32[3], "");
+                        break;
+                    case VSIR_DATA_F16:
+                        shader_print_f16_literal(compiler, "", reg->u.immconst_u32[0], "");
+                        shader_print_f16_literal(compiler, ", ", reg->u.immconst_u32[1], "");
+                        shader_print_f16_literal(compiler, ", ", reg->u.immconst_u32[2], "");
+                        shader_print_f16_literal(compiler, ", ", reg->u.immconst_u32[3], "");
+                        break;
                     case VSIR_DATA_F32:
                         if (untyped)
                         {
@@ -791,12 +824,24 @@ static void shader_print_register(struct vkd3d_d3d_asm_compiler *compiler, const
                             shader_print_float_literal(compiler, ", ", reg->u.immconst_f32[3], "");
                         }
                         break;
+                    case VSIR_DATA_I16:
+                        shader_print_i16_literal(compiler, "", reg->u.immconst_u32[0], "");
+                        shader_print_i16_literal(compiler, ", ", reg->u.immconst_u32[1], "");
+                        shader_print_i16_literal(compiler, ", ", reg->u.immconst_u32[2], "");
+                        shader_print_i16_literal(compiler, ", ", reg->u.immconst_u32[3], "");
+                        break;
                     case VSIR_DATA_I32:
                         shader_print_int_literal(compiler, "", reg->u.immconst_u32[0], "");
                         shader_print_int_literal(compiler, ", ", reg->u.immconst_u32[1], "");
                         shader_print_int_literal(compiler, ", ", reg->u.immconst_u32[2], "");
                         shader_print_int_literal(compiler, ", ", reg->u.immconst_u32[3], "");
                         break;
+                    case VSIR_DATA_U16:
+                        shader_print_u16_literal(compiler, "", reg->u.immconst_u32[0], "");
+                        shader_print_u16_literal(compiler, ", ", reg->u.immconst_u32[1], "");
+                        shader_print_u16_literal(compiler, ", ", reg->u.immconst_u32[2], "");
+                        shader_print_u16_literal(compiler, ", ", reg->u.immconst_u32[3], "");
+                        break;
                     case VSIR_DATA_U32:
                         shader_print_uint_literal(compiler, "", reg->u.immconst_u32[0], "");
                         shader_print_uint_literal(compiler, ", ", reg->u.immconst_u32[1], "");
@@ -926,7 +971,7 @@ static void shader_print_register(struct vkd3d_d3d_asm_compiler *compiler, const
     vkd3d_string_buffer_printf(buffer, "%s", suffix);
 }
 
-static void shader_print_precision(struct vkd3d_d3d_asm_compiler *compiler, const struct vkd3d_shader_register *reg)
+static void shader_print_precision(struct vkd3d_d3d_asm_compiler *compiler, const struct vsir_operand *reg)
 {
     struct vkd3d_string_buffer *buffer = &compiler->buffer;
     const char *precision;
@@ -960,7 +1005,7 @@ static void shader_print_precision(struct vkd3d_d3d_asm_compiler *compiler, cons
     vkd3d_string_buffer_printf(buffer, " {%s%s%s}", compiler->colours.modifier, precision, compiler->colours.reset);
 }
 
-static void shader_print_non_uniform(struct vkd3d_d3d_asm_compiler *compiler, const struct vkd3d_shader_register *reg)
+static void shader_print_non_uniform(struct vkd3d_d3d_asm_compiler *compiler, const struct vsir_operand *reg)
 {
     if (reg->non_uniform)
         vkd3d_string_buffer_printf(&compiler->buffer, " {%snonuniform%s}",
@@ -968,7 +1013,7 @@ static void shader_print_non_uniform(struct vkd3d_d3d_asm_compiler *compiler, co
 }
 
 static void shader_print_reg_type(struct vkd3d_d3d_asm_compiler *compiler,
-        const char *prefix, const struct vkd3d_shader_register *reg, const char *suffix)
+        const char *prefix, const struct vsir_operand *reg, const char *suffix)
 {
     static const char *dimensions[] =
     {
@@ -994,9 +1039,9 @@ static void shader_print_reg_type(struct vkd3d_d3d_asm_compiler *compiler,
     else
         dimension = "??";
 
-    vkd3d_string_buffer_printf(buffer, "%s <%s", prefix, dimension);
+    vkd3d_string_buffer_printf(buffer, " %s<%s%s", prefix, compiler->colours.type, dimension);
     shader_print_data_type(compiler, reg->data_type);
-    vkd3d_string_buffer_printf(buffer, ">%s", suffix);
+    vkd3d_string_buffer_printf(buffer, "%s>%s", compiler->colours.reset, suffix);
 }
 
 static void shader_print_indexable_temp_data_type(struct vkd3d_d3d_asm_compiler *compiler,
@@ -1046,7 +1091,7 @@ static void shader_print_dst_operand(struct vkd3d_d3d_asm_compiler *compiler,
 {
     uint32_t write_mask = dst->write_mask;
 
-    shader_print_register(compiler, prefix, &dst->reg, is_declaration, "");
+    shader_print_operand(compiler, prefix, &dst->reg, is_declaration, "");
 
     if (write_mask && dst->reg.dimension == VSIR_DIMENSION_VEC4)
     {
@@ -1085,7 +1130,7 @@ static void shader_print_src_operand(struct vkd3d_d3d_asm_compiler *compiler,
     if (src_modifier == VKD3DSPSM_ABS || src_modifier == VKD3DSPSM_ABSNEG)
         is_abs = true;
 
-    shader_print_register(compiler, is_abs ? "|" : "", &src->reg, false, "");
+    shader_print_operand(compiler, is_abs ? "|" : "", &src->reg, false, "");
 
     switch (src_modifier)
     {
@@ -1524,12 +1569,12 @@ static void shader_dump_instruction(struct vkd3d_d3d_asm_compiler *compiler,
             shader_print_dcl_usage(compiler, "_", &ins->declaration.semantic, ins->flags, "");
             shader_dump_ins_modifiers(compiler, &ins->declaration.semantic.resource.reg);
             vkd3d_string_buffer_printf(buffer, "%s", compiler->colours.reset);
-            shader_print_register(compiler, " ", &ins->declaration.semantic.resource.reg.reg, true, "");
+            shader_print_operand(compiler, " ", &ins->declaration.semantic.resource.reg.reg, true, "");
             shader_dump_register_space(compiler, ins->declaration.semantic.resource.range.space);
             break;
 
         case VSIR_OP_DCL_CONSTANT_BUFFER:
-            shader_print_register(compiler, " ", &ins->declaration.cb.src.reg, true, "");
+            shader_print_operand(compiler, " ", &ins->declaration.cb.src.reg, true, "");
             if (vkd3d_shader_ver_ge(&compiler->shader_version, 6, 0))
                 shader_print_subscript(compiler, ins->declaration.cb.size, NULL);
             else if (vkd3d_shader_ver_ge(&compiler->shader_version, 5, 1))
@@ -1626,7 +1671,7 @@ static void shader_dump_instruction(struct vkd3d_d3d_asm_compiler *compiler,
             break;
 
         case VSIR_OP_DCL_SAMPLER:
-            shader_print_register(compiler, " ", &ins->declaration.sampler.src.reg, true,
+            shader_print_operand(compiler, " ", &ins->declaration.sampler.src.reg, true,
                     ins->flags == VKD3DSI_SAMPLER_COMPARISON_MODE ? ", comparisonMode" : "");
             shader_dump_register_space(compiler, ins->declaration.sampler.range.space);
             break;
@@ -1937,16 +1982,16 @@ static void shader_print_descriptor_name(struct vkd3d_d3d_asm_compiler *compiler
     switch (t)
     {
         case VKD3D_SHADER_DESCRIPTOR_TYPE_SRV:
-            type = shader_register_names[VKD3DSPR_RESOURCE];
+            type = vsir_register_type_get_name(VKD3DSPR_RESOURCE, NULL);
             break;
         case VKD3D_SHADER_DESCRIPTOR_TYPE_UAV:
-            type = shader_register_names[VKD3DSPR_UAV];
+            type = vsir_register_type_get_name(VKD3DSPR_UAV, NULL);
             break;
         case VKD3D_SHADER_DESCRIPTOR_TYPE_CBV:
-            type = shader_register_names[VKD3DSPR_CONSTBUFFER];
+            type = vsir_register_type_get_name(VKD3DSPR_CONSTBUFFER, NULL);
             break;
         case VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER:
-            type = shader_register_names[VKD3DSPR_SAMPLER];
+            type = vsir_register_type_get_name(VKD3DSPR_SAMPLER, NULL);
             break;
         case VKD3D_SHADER_DESCRIPTOR_TYPE_FORCE_32BIT:
             break;
@@ -2021,28 +2066,32 @@ enum vkd3d_result d3d_asm_compile(struct vsir_program *program, const struct vkd
     static const struct vkd3d_d3d_asm_colours no_colours =
     {
         .reset = "",
+        .enumerant = "",
         .error = "",
+        .label = "",
         .literal = "",
         .modifier = "",
         .opcode = "",
         .reg = "",
         .swizzle = "",
+        .type = "",
         .version = "",
         .write_mask = "",
-        .label = "",
     };
     static const struct vkd3d_d3d_asm_colours colours =
     {
         .reset = "\x1b[m",
+        .enumerant = "\x1b[93m",
         .error = "\x1b[97;41m",
+        .label = "\x1b[91m",
         .literal = "\x1b[95m",
         .modifier = "\x1b[36m",
         .opcode = "\x1b[96;1m",
         .reg = "\x1b[96m",
         .swizzle = "\x1b[93m",
+        .type = "\x1b[92m",
         .version = "\x1b[36m",
         .write_mask = "\x1b[93m",
-        .label = "\x1b[91m",
     };
 
     formatting = VKD3D_SHADER_COMPILE_OPTION_FORMATTING_INDENT
@@ -2090,9 +2139,9 @@ enum vkd3d_result d3d_asm_compile(struct vsir_program *program, const struct vkd
 
     if (compiler.flags & VSIR_ASM_FLAG_DUMP_DENORM_MODES)
         vkd3d_string_buffer_printf(buffer, ".denorm %s, %s, %s\n",
-                vsir_denorm_mode_get_name(program->f16_denorm_mode, "??"),
-                vsir_denorm_mode_get_name(program->f32_denorm_mode, "??"),
-                vsir_denorm_mode_get_name(program->f64_denorm_mode, "??"));
+                vsir_denorm_mode_get_name(program->f16_denormal_mode, "??"),
+                vsir_denorm_mode_get_name(program->f32_denormal_mode, "??"),
+                vsir_denorm_mode_get_name(program->f64_denormal_mode, "??"));
 
     if (compiler.flags & VSIR_ASM_FLAG_DUMP_SIGNATURES
             && (result = dump_dxbc_signatures(&compiler, program)) < 0)
diff --git a/libs/vkd3d/libs/vkd3d-shader/d3dbc.c b/libs/vkd3d/libs/vkd3d-shader/d3dbc.c
index 8784ea69e978..37d6f64964d5 100644
--- a/libs/vkd3d/libs/vkd3d-shader/d3dbc.c
+++ b/libs/vkd3d/libs/vkd3d-shader/d3dbc.c
@@ -560,15 +560,15 @@ static enum vkd3d_shader_register_type parse_register_type(
     return VKD3DSPR_INVALID;
 }
 
-static void d3dbc_parse_register(struct vkd3d_shader_sm1_parser *d3dbc,
-        struct vkd3d_shader_register *reg, uint32_t param, struct vsir_src_operand *rel_addr)
+static void d3dbc_parse_operand(struct vkd3d_shader_sm1_parser *d3dbc,
+        struct vsir_operand *reg, uint32_t param, struct vsir_src_operand *rel_addr)
 {
     enum vkd3d_shader_register_type reg_type;
     unsigned int index_offset, idx_count;
 
     reg_type = parse_register_type(d3dbc, param, &index_offset);
     idx_count = idx_count_from_reg_type(reg_type);
-    vsir_register_init(reg, reg_type, VSIR_DATA_F32, idx_count);
+    vsir_operand_init(reg, reg_type, VSIR_DATA_F32, idx_count);
     reg->precision = VKD3D_SHADER_REGISTER_PRECISION_DEFAULT;
     reg->non_uniform = false;
     if (idx_count == 1)
@@ -576,7 +576,7 @@ static void d3dbc_parse_register(struct vkd3d_shader_sm1_parser *d3dbc,
         reg->idx[0].offset = index_offset + (param & VKD3D_SM1_REGISTER_NUMBER_MASK);
         reg->idx[0].rel_addr = rel_addr;
     }
-    if (reg->type == VKD3DSPR_SAMPLER)
+    if (reg->type == VKD3DSPR_COMBINED_SAMPLER)
         reg->dimension = VSIR_DIMENSION_NONE;
     else if (reg->type == VKD3DSPR_DEPTHOUT)
         reg->dimension = VSIR_DIMENSION_SCALAR;
@@ -592,15 +592,18 @@ static void d3dbc_parse_register(struct vkd3d_shader_sm1_parser *d3dbc,
 static void d3dbc_parse_src_operand(struct vkd3d_shader_sm1_parser *d3dbc,
         uint32_t param, struct vsir_src_operand *rel_addr, struct vsir_src_operand *src)
 {
-    d3dbc_parse_register(d3dbc, &src->reg, param, rel_addr);
-    src->swizzle = swizzle_from_sm1((param & VKD3D_SM1_SWIZZLE_MASK) >> VKD3D_SM1_SWIZZLE_SHIFT);
+    d3dbc_parse_operand(d3dbc, &src->reg, param, rel_addr);
+    if (src->reg.dimension == VSIR_DIMENSION_VEC4)
+        src->swizzle = swizzle_from_sm1((param & VKD3D_SM1_SWIZZLE_MASK) >> VKD3D_SM1_SWIZZLE_SHIFT);
+    else
+        src->swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X);
     src->modifiers = (param & VKD3D_SM1_SRC_MODIFIER_MASK) >> VKD3D_SM1_SRC_MODIFIER_SHIFT;
 }
 
 static void d3dbc_parse_dst_operand(struct vkd3d_shader_sm1_parser *d3dbc,
         uint32_t param, struct vsir_src_operand *rel_addr, struct vsir_dst_operand *dst)
 {
-    d3dbc_parse_register(d3dbc, &dst->reg, param, rel_addr);
+    d3dbc_parse_operand(d3dbc, &dst->reg, param, rel_addr);
     dst->modifiers = (param & VKD3D_SM1_DST_MODIFIER_MASK) >> VKD3D_SM1_DST_MODIFIER_SHIFT;
     dst->shift = (param & VKD3D_SM1_DSTSHIFT_MASK) >> VKD3D_SM1_DSTSHIFT_SHIFT;
 
@@ -757,8 +760,8 @@ static void add_signature_mask(struct vkd3d_shader_sm1_parser *sm1, bool output,
         element->used_mask &= element->mask;
 }
 
-static bool add_signature_element_from_register(struct vkd3d_shader_sm1_parser *sm1,
-        const struct vkd3d_shader_register *reg, bool is_dcl, unsigned int mask, uint32_t dst_modifiers)
+static bool add_signature_element_from_operand(struct vkd3d_shader_sm1_parser *sm1,
+        const struct vsir_operand *reg, bool is_dcl, unsigned int mask, uint32_t dst_modifiers)
 {
     const struct vkd3d_shader_version *version = &sm1->program->shader_version;
     unsigned int register_index = reg->idx_count > 0 ? reg->idx[0].offset : 0;
@@ -868,8 +871,8 @@ static bool add_signature_element_from_semantic(struct vkd3d_shader_sm1_parser *
         const struct vkd3d_shader_semantic *semantic)
 {
     const struct vkd3d_shader_version *version = &sm1->program->shader_version;
-    const struct vkd3d_shader_register *reg = &semantic->resource.reg.reg;
     enum vkd3d_shader_sysval_semantic sysval = VKD3D_SHADER_SV_NONE;
+    const struct vsir_operand *reg = &semantic->resource.reg.reg;
     unsigned int mask = semantic->resource.reg.write_mask;
     uint32_t modifiers = semantic->resource.reg.modifiers;
     bool output;
@@ -897,11 +900,11 @@ static bool add_signature_element_from_semantic(struct vkd3d_shader_sm1_parser *
     else if (reg->type == VKD3DSPR_INPUT || reg->type == VKD3DSPR_TEXTURE)
         output = false;
     else /* vpos and vface don't have a semantic. */
-        return add_signature_element_from_register(sm1, reg, true, mask, modifiers);
+        return add_signature_element_from_operand(sm1, reg, true, mask, modifiers);
 
     /* sm2 pixel shaders use DCL but don't provide a semantic. */
     if (version->type == VKD3D_SHADER_TYPE_PIXEL && version->major == 2)
-        return add_signature_element_from_register(sm1, reg, true, mask, modifiers);
+        return add_signature_element_from_operand(sm1, reg, true, mask, modifiers);
 
     /* With the exception of vertex POSITION output, none of these are system
      * values. Pixel POSITION input is not equivalent to SV_Position; the closer
@@ -927,42 +930,77 @@ static void record_constant_register(struct vkd3d_shader_sm1_parser *sm1,
     }
 }
 
-static void shader_sm1_scan_register(struct vkd3d_shader_sm1_parser *sm1,
-        const struct vkd3d_shader_register *reg, unsigned int mask, bool from_def)
+static unsigned int d3dbc_get_src_register_count(enum vkd3d_shader_opcode opcode, unsigned int src_idx)
 {
-    struct vsir_program *program = sm1->program;
-    uint32_t register_index = reg->idx[0].offset;
+    switch (opcode)
+    {
+        case VSIR_OP_M3x2:
+            return (src_idx == 1) ? 2 : 1;
+        case VSIR_OP_M3x3:
+        case VSIR_OP_M4x3:
+            return (src_idx == 1) ? 3 : 1;
+        case VSIR_OP_M3x4:
+        case VSIR_OP_M4x4:
+            return (src_idx == 1) ? 4 : 1;
+        default:
+            return 1;
+    }
+}
 
-    switch (reg->type)
+static void d3dbc_scan_register(struct vkd3d_shader_sm1_parser *d3dbc,
+        const struct vsir_operand *reg0, unsigned int mask, bool from_def, unsigned int count)
+{
+    struct vsir_program *program = d3dbc->program;
+    struct vsir_operand reg = *reg0;
+
+    for (unsigned int i = 0; i < count; ++i)
     {
-        case VKD3DSPR_TEMP:
-            program->temp_count = max(program->temp_count, register_index + 1);
-            break;
+        uint32_t register_index = reg.idx[0].offset;
 
-        case VKD3DSPR_CONST:
-            record_constant_register(sm1, VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, register_index, from_def);
-            break;
+        switch (reg.type)
+        {
+            case VKD3DSPR_TEMP:
+                program->temp_count = max(program->temp_count, register_index + 1);
+                break;
 
-        case VKD3DSPR_CONSTINT:
-            record_constant_register(sm1, VKD3D_SHADER_D3DBC_INT_CONSTANT_REGISTER, register_index, from_def);
-            break;
+            case VKD3DSPR_CONST:
+                record_constant_register(d3dbc, VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, register_index, from_def);
+                break;
 
-        case VKD3DSPR_CONSTBOOL:
-            record_constant_register(sm1, VKD3D_SHADER_D3DBC_BOOL_CONSTANT_REGISTER, register_index, from_def);
-            break;
+            case VKD3DSPR_CONSTINT:
+                record_constant_register(d3dbc, VKD3D_SHADER_D3DBC_INT_CONSTANT_REGISTER, register_index, from_def);
+                break;
 
-        default:
-            break;
-    }
+            case VKD3DSPR_CONSTBOOL:
+                record_constant_register(d3dbc, VKD3D_SHADER_D3DBC_BOOL_CONSTANT_REGISTER, register_index, from_def);
+                break;
+
+            case VKD3DSPR_DEPTHOUT:
+                bitmap_set(program->io_dcls, VKD3DSPR_DEPTHOUT);
+                break;
 
-    add_signature_element_from_register(sm1, reg, false, mask, 0);
+            case VKD3DSPR_RASTOUT:
+                if (register_index == VSIR_RASTOUT_POINT_SIZE)
+                    program->has_point_size = true;
+                if (register_index == VSIR_RASTOUT_FOG)
+                    program->has_fog = true;
+                break;
+
+            default:
+                break;
+        }
+
+        add_signature_element_from_operand(d3dbc, &reg, false, mask, 0);
+
+        ++reg.idx[0].offset;
+    }
 }
 
 static void d3dbc_add_combined_sampler_descriptor(struct vkd3d_shader_sm1_parser *d3dbc,
         unsigned int sampler_idx, enum vkd3d_shader_resource_type resource_type)
 {
-    struct vkd3d_shader_register_range range = {.first = sampler_idx, .last = sampler_idx};
     const struct vkd3d_shader_d3dbc_source_info *source_info = d3dbc->d3dbc_source_info;
+    struct vsir_register_range range = {.first = sampler_idx, .last = sampler_idx};
     struct vsir_program *program = d3dbc->program;
     struct vkd3d_shader_descriptor_info1 *d;
 
@@ -1095,18 +1133,13 @@ static void d3dbc_read_dst_operand(struct vkd3d_shader_sm1_parser *d3dbc,
         d3dbc_parse_src_operand(d3dbc, addr_token, NULL, dst_rel_addr);
     }
     d3dbc_parse_dst_operand(d3dbc, token, dst_rel_addr, dst);
-
-    if (dst->reg.type == VKD3DSPR_RASTOUT && dst->reg.idx[0].offset == VSIR_RASTOUT_POINT_SIZE)
-        d3dbc->program->has_point_size = true;
-    if (dst->reg.type == VKD3DSPR_RASTOUT && dst->reg.idx[0].offset == VSIR_RASTOUT_FOG)
-        d3dbc->program->has_fog = true;
 }
 
 static void shader_sm1_read_semantic(struct vkd3d_shader_sm1_parser *sm1,
         const uint32_t **ptr, struct vkd3d_shader_semantic *semantic)
 {
     enum vkd3d_sm1_resource_type resource_type;
-    struct vkd3d_shader_register_range *range;
+    struct vsir_register_range *range;
     uint32_t usage_token, dst_token;
 
     if (*ptr >= sm1->end || sm1->end - *ptr < 2)
@@ -1148,6 +1181,9 @@ static void shader_sm1_read_semantic(struct vkd3d_shader_sm1_parser *sm1,
         d3dbc_add_combined_sampler_descriptor(sm1, range->first, semantic->resource_type);
         sm1->texture_descriptors |= (1u << range->first);
     }
+
+    if (semantic->usage == VKD3D_DECL_USAGE_PSIZE)
+        sm1->program->has_point_size = true;
 }
 
 static void shader_sm1_read_immconst(struct vkd3d_shader_sm1_parser *sm1, const uint32_t **ptr,
@@ -1167,7 +1203,8 @@ static void shader_sm1_read_immconst(struct vkd3d_shader_sm1_parser *sm1, const
     vsir_src_operand_init(src, VKD3DSPR_IMMCONST, data_type, 0);
     src->reg.dimension = dimension;
     memcpy(src->reg.u.immconst_u32, *ptr, count * sizeof(uint32_t));
-    src->swizzle = VKD3D_SHADER_NO_SWIZZLE;
+    if (dimension == VSIR_DIMENSION_VEC4)
+        src->swizzle = VKD3D_SHADER_NO_SWIZZLE;
 
     *ptr += count;
 }
@@ -1372,31 +1409,31 @@ static void shader_sm1_read_instruction(struct vkd3d_shader_sm1_parser *sm1, str
     {
         d3dbc_read_dst_operand(sm1, &p, dst);
         shader_sm1_read_immconst(sm1, &p, &src[0], VSIR_DIMENSION_VEC4, VSIR_DATA_F32);
-        shader_sm1_scan_register(sm1, &dst->reg, dst->write_mask, true);
+        d3dbc_scan_register(sm1, &dst->reg, dst->write_mask, true, 1);
     }
     else if (ins->opcode == VSIR_OP_DEFB)
     {
         d3dbc_read_dst_operand(sm1, &p, dst);
         shader_sm1_read_immconst(sm1, &p, &src[0], VSIR_DIMENSION_SCALAR, VSIR_DATA_U32);
-        shader_sm1_scan_register(sm1, &dst->reg, dst->write_mask, true);
+        d3dbc_scan_register(sm1, &dst->reg, dst->write_mask, true, 1);
     }
     else if (ins->opcode == VSIR_OP_DEFI)
     {
         d3dbc_read_dst_operand(sm1, &p, dst);
         shader_sm1_read_immconst(sm1, &p, &src[0], VSIR_DIMENSION_VEC4, VSIR_DATA_I32);
-        shader_sm1_scan_register(sm1, &dst->reg, dst->write_mask, true);
+        d3dbc_scan_register(sm1, &dst->reg, dst->write_mask, true, 1);
     }
     else if (ins->opcode == VSIR_OP_TEXKILL)
     {
         /* TEXKILL, uniquely, encodes its argument as a destination, when it is
          * semantically a source. Since we have multiple passes which operate
          * generically on sources or destinations, normalize that. */
-        const struct vkd3d_shader_register *reg;
         struct vsir_dst_operand tmp_dst;
+        const struct vsir_operand *reg;
 
         reg = &tmp_dst.reg;
         d3dbc_read_dst_operand(sm1, &p, &tmp_dst);
-        shader_sm1_scan_register(sm1, reg, tmp_dst.write_mask, false);
+        d3dbc_scan_register(sm1, reg, tmp_dst.write_mask, false, 1);
 
         vsir_src_operand_init(&src[0], reg->type, reg->data_type, reg->idx_count);
         src[0].reg = *reg;
@@ -1411,7 +1448,7 @@ static void shader_sm1_read_instruction(struct vkd3d_shader_sm1_parser *sm1, str
         if (ins->dst_count)
         {
             d3dbc_read_dst_operand(sm1, &p, dst);
-            shader_sm1_scan_register(sm1, &dst->reg, dst->write_mask, false);
+            d3dbc_scan_register(sm1, &dst->reg, dst->write_mask, false, 1);
         }
 
         /* Predication token */
@@ -1421,8 +1458,10 @@ static void shader_sm1_read_instruction(struct vkd3d_shader_sm1_parser *sm1, str
         /* Other source tokens */
         for (i = 0; i < ins->src_count; ++i)
         {
+            unsigned int src_reg_count = d3dbc_get_src_register_count(ins->opcode, i);
+
             d3dbc_read_src_operand(sm1, &p, &src[i]);
-            shader_sm1_scan_register(sm1, &src[i].reg, mask_from_swizzle(src[i].swizzle), false);
+            d3dbc_scan_register(sm1, &src[i].reg, mask_from_swizzle(src[i].swizzle), false, src_reg_count);
         }
     }
 
@@ -1527,7 +1566,7 @@ static enum vkd3d_result shader_sm1_init(struct vkd3d_shader_sm1_parser *sm1, st
             code_size != ~(size_t)0 ? token_count / 4u + 4 : 16, VSIR_CF_STRUCTURED, normalisation_level))
         return VKD3D_ERROR_OUT_OF_MEMORY;
 
-    program->f32_denorm_mode = VSIR_DENORM_FLUSH_TO_ZERO;
+    program->f32_denormal_mode = VKD3D_SHADER_DENORMAL_MODE_FLUSH_TO_ZERO;
 
     vkd3d_shader_parser_init(&sm1->p, message_context, compile_info->source_name);
     sm1->program = program;
@@ -1588,7 +1627,7 @@ int d3dbc_parse(const struct vkd3d_shader_compile_info *compile_info, uint64_t c
 
     for (i = 0; i < 3; ++i)
     {
-        struct vkd3d_shader_register_range range = {.space = 0, .first = i, .last = i};
+        struct vsir_register_range range = {.space = 0, .first = i, .last = i};
         unsigned int size = get_external_constant_count(&sm1, i);
 
         if (size)
@@ -1823,7 +1862,7 @@ static void d3dbc_write_comment(struct d3dbc_compiler *d3dbc,
     set_u32(buffer, offset, vkd3d_make_u32(VKD3D_SM1_OP_COMMENT, (end - start) / sizeof(uint32_t)));
 }
 
-static enum vkd3d_sm1_register_type d3dbc_register_type_from_vsir(const struct vkd3d_shader_register *reg)
+static enum vkd3d_sm1_register_type d3dbc_register_type_from_vsir(const struct vsir_operand *reg)
 {
     if (reg->type == VKD3DSPR_CONST)
     {
@@ -1844,7 +1883,7 @@ static enum vkd3d_sm1_register_type d3dbc_register_type_from_vsir(const struct v
     vkd3d_unreachable();
 }
 
-static uint32_t sm1_encode_register_type(const struct vkd3d_shader_register *reg)
+static uint32_t sm1_encode_register_type(const struct vsir_operand *reg)
 {
     enum vkd3d_sm1_register_type sm1_type = d3dbc_register_type_from_vsir(reg);
 
@@ -1925,6 +1964,27 @@ static void write_sm1_src_register(struct vkd3d_bytecode_buffer *buffer, const s
             | (offset & VKD3D_SM1_REGISTER_NUMBER_MASK));
 }
 
+static void validate_register_limits(struct d3dbc_compiler *d3dbc,
+        const struct vsir_operand *reg, const struct vkd3d_shader_location *loc)
+{
+    const struct vkd3d_shader_version *version = &d3dbc->program->shader_version;
+    uint32_t idx = reg->idx[0].offset;
+
+    if (reg->type == VKD3DSPR_TEMP)
+    {
+        if ((version->type == VKD3D_SHADER_TYPE_PIXEL && vkd3d_shader_ver_le(version, 1, 3) && idx >= 2)
+                || (version->type == VKD3D_SHADER_TYPE_PIXEL && vkd3d_shader_ver_le(version, 1, 4) && idx >= 6)
+                || (version->type == VKD3D_SHADER_TYPE_VERTEX && vkd3d_shader_ver_le(version, 2, 0) && idx >= 12)
+                || idx >= 32)
+        {
+            vkd3d_shader_error(d3dbc->message_context, loc, VKD3D_SHADER_ERROR_VSIR_INVALID_INDEX,
+                    "Register r%u exceeds limits for shader type %#x, version %u.%u.",
+                    idx, version->type, version->major, version->minor);
+            d3dbc->failed = true;
+        }
+    }
+}
+
 static void d3dbc_write_instruction(struct d3dbc_compiler *d3dbc, const struct vkd3d_shader_instruction *ins)
 {
     const struct vkd3d_shader_version *version = &d3dbc->program->shader_version;
@@ -1948,6 +2008,8 @@ static void d3dbc_write_instruction(struct d3dbc_compiler *d3dbc, const struct v
 
     for (i = 0; i < ins->dst_count; ++i)
     {
+        validate_register_limits(d3dbc, &ins->dst[i].reg, &ins->location);
+
         if (ins->dst[i].reg.idx[0].rel_addr)
         {
             vkd3d_shader_error(d3dbc->message_context, &ins->location, VKD3D_SHADER_ERROR_D3DBC_NOT_IMPLEMENTED,
@@ -1959,6 +2021,8 @@ static void d3dbc_write_instruction(struct d3dbc_compiler *d3dbc, const struct v
 
     for (i = 0; i < ins->src_count; ++i)
     {
+        validate_register_limits(d3dbc, &ins->src[i].reg, &ins->location);
+
         src = &ins->src[i];
         write_sm1_src_register(buffer, src);
         if (src->reg.idx_count && src->reg.idx[0].rel_addr)
@@ -1976,7 +2040,7 @@ static void d3dbc_write_instruction(struct d3dbc_compiler *d3dbc, const struct v
 
 static void d3dbc_write_texkill(struct d3dbc_compiler *d3dbc, const struct vkd3d_shader_instruction *ins)
 {
-    const struct vkd3d_shader_register *reg = &ins->src[0].reg;
+    const struct vsir_operand *reg = &ins->src[0].reg;
     struct vkd3d_shader_instruction tmp;
     struct vsir_dst_operand dst;
 
@@ -2019,6 +2083,30 @@ static void d3dbc_write_vsir_def(struct d3dbc_compiler *d3dbc, const struct vkd3
         put_f32(buffer, ins->src[0].reg.u.immconst_f32[x]);
 }
 
+static void d3dbc_write_vsir_defi(struct d3dbc_compiler *d3dbc, const struct vkd3d_shader_instruction *ins)
+{
+    const struct vkd3d_shader_version *version = &d3dbc->program->shader_version;
+    struct vkd3d_bytecode_buffer *buffer = &d3dbc->buffer;
+    uint32_t token;
+
+    const struct vsir_dst_operand reg =
+    {
+        .reg.type = VKD3DSPR_CONSTINT,
+        .write_mask = VKD3DSP_WRITEMASK_ALL,
+        .reg.idx[0].offset = ins->dst[0].reg.idx[0].offset,
+        .reg.idx_count = 1,
+    };
+
+    token = VKD3D_SM1_OP_DEFI;
+    if (version->major > 1)
+        token |= 5 << VKD3D_SM1_INSTRUCTION_LENGTH_SHIFT;
+    put_u32(buffer, token);
+
+    write_sm1_dst_register(buffer, &reg);
+    for (unsigned int x = 0; x < 4; ++x)
+        put_u32(buffer, ins->src[0].reg.u.immconst_u32[x]);
+}
+
 static void d3dbc_write_vsir_sampler_dcl(struct d3dbc_compiler *d3dbc,
         unsigned int reg_id, enum vkd3d_sm1_resource_type res_type)
 {
@@ -2098,6 +2186,10 @@ static void d3dbc_write_vsir_instruction(struct d3dbc_compiler *d3dbc, const str
             d3dbc_write_vsir_def(d3dbc, ins);
             break;
 
+        case VSIR_OP_DEFI:
+            d3dbc_write_vsir_defi(d3dbc, ins);
+            break;
+
         case VSIR_OP_DCL:
             d3dbc_write_vsir_dcl(d3dbc, ins);
             break;
@@ -2108,6 +2200,7 @@ static void d3dbc_write_vsir_instruction(struct d3dbc_compiler *d3dbc, const str
 
         case VSIR_OP_ABS:
         case VSIR_OP_ADD:
+        case VSIR_OP_BREAK:
         case VSIR_OP_CMP:
         case VSIR_OP_DP2ADD:
         case VSIR_OP_DP3:
@@ -2116,6 +2209,7 @@ static void d3dbc_write_vsir_instruction(struct d3dbc_compiler *d3dbc, const str
         case VSIR_OP_DSY:
         case VSIR_OP_ELSE:
         case VSIR_OP_ENDIF:
+        case VSIR_OP_ENDREP:
         case VSIR_OP_FRC:
         case VSIR_OP_IFC:
         case VSIR_OP_MAD:
@@ -2124,6 +2218,7 @@ static void d3dbc_write_vsir_instruction(struct d3dbc_compiler *d3dbc, const str
         case VSIR_OP_MOV:
         case VSIR_OP_MOVA:
         case VSIR_OP_MUL:
+        case VSIR_OP_REP:
         case VSIR_OP_SINCOS:
         case VSIR_OP_SLT:
         case VSIR_OP_TEXLD:
diff --git a/libs/vkd3d/libs/vkd3d-shader/dxbc.c b/libs/vkd3d/libs/vkd3d-shader/dxbc.c
index 52448e89e373..4a087f9fe867 100644
--- a/libs/vkd3d/libs/vkd3d-shader/dxbc.c
+++ b/libs/vkd3d/libs/vkd3d-shader/dxbc.c
@@ -186,7 +186,6 @@ static int parse_dxbc(const struct vkd3d_shader_code *dxbc, struct vkd3d_shader_
 
     if (data_size < VKD3D_DXBC_HEADER_SIZE)
     {
-        WARN("Invalid data size %zu.\n", data_size);
         vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXBC_INVALID_SIZE,
                 "DXBC size %zu is smaller than the DXBC header size.", data_size);
         return VKD3D_ERROR_INVALID_ARGUMENT;
@@ -197,7 +196,6 @@ static int parse_dxbc(const struct vkd3d_shader_code *dxbc, struct vkd3d_shader_
 
     if (tag != TAG_DXBC)
     {
-        WARN("Wrong tag.\n");
         vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXBC_INVALID_MAGIC, "Invalid DXBC magic.");
         return VKD3D_ERROR_INVALID_ARGUMENT;
     }
@@ -226,7 +224,6 @@ static int parse_dxbc(const struct vkd3d_shader_code *dxbc, struct vkd3d_shader_
     TRACE("version: %#x.\n", version);
     if (version != 0x00000001)
     {
-        WARN("Got unexpected DXBC version %#x.\n", version);
         vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXBC_INVALID_VERSION,
                 "DXBC version %#x is not supported.", version);
         return VKD3D_ERROR_INVALID_ARGUMENT;
@@ -255,7 +252,6 @@ static int parse_dxbc(const struct vkd3d_shader_code *dxbc, struct vkd3d_shader_
 
         if (chunk_offset >= data_size || !require_space(chunk_offset, 2, sizeof(uint32_t), data_size))
         {
-            WARN("Invalid chunk offset %#x (data size %zu).\n", chunk_offset, data_size);
             vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXBC_INVALID_CHUNK_OFFSET,
                     "DXBC chunk %u has invalid offset %#x (data size %#zx).", i, chunk_offset, data_size);
             vkd3d_free(sections);
@@ -269,8 +265,6 @@ static int parse_dxbc(const struct vkd3d_shader_code *dxbc, struct vkd3d_shader_
 
         if (!require_space(chunk_ptr - data, 1, chunk_size, data_size))
         {
-            WARN("Invalid chunk size %#x (data size %zu, chunk offset %#x).\n",
-                    chunk_size, data_size, chunk_offset);
             vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXBC_INVALID_CHUNK_SIZE,
                     "DXBC chunk %u has invalid size %#x (data size %#zx, chunk offset %#x).",
                     i, chunk_offset, data_size, chunk_offset);
@@ -381,7 +375,6 @@ static int shader_parse_signature(const struct vkd3d_shader_dxbc_section_desc *s
 
     if (!require_space(0, 2, sizeof(uint32_t), section->data.size))
     {
-        WARN("Invalid data size %#zx.\n", section->data.size);
         vkd3d_shader_error(message_context, NULL, VKD3D_SHADER_ERROR_DXBC_INVALID_SIGNATURE,
                 "Section size %zu is smaller than the minimum signature header size.", section->data.size);
         return VKD3D_ERROR_INVALID_ARGUMENT;
@@ -395,7 +388,6 @@ static int shader_parse_signature(const struct vkd3d_shader_dxbc_section_desc *s
     if (align(header_size, sizeof(uint32_t)) != header_size || i < 2
             || !require_space(2, i - 2, sizeof(uint32_t), section->data.size))
     {
-        WARN("Invalid header size %#x.\n", header_size);
         vkd3d_shader_error(message_context, NULL, VKD3D_SHADER_ERROR_DXBC_INVALID_SIGNATURE,
                 "Signature header size %#x is invalid.", header_size);
         return VKD3D_ERROR_INVALID_ARGUMENT;
@@ -524,7 +516,6 @@ static int shdr_parse_features(const struct vkd3d_shader_dxbc_section_desc *sect
 
     if (!require_space(0, 1, sizeof(uint64_t), section->data.size))
     {
-        WARN("Invalid data size %#zx.\n", section->data.size);
         vkd3d_shader_error(message_context, NULL, VKD3D_SHADER_ERROR_DXBC_INVALID_CHUNK_SIZE,
                 "SFI0 section size %zu is too small to contain flags.", section->data.size);
         return VKD3D_ERROR_INVALID_ARGUMENT;
@@ -1323,7 +1314,6 @@ static int shader_write_root_parameters(struct root_signature_writer_context *co
                     shader_write_root_descriptor1(buffer, &desc->u.v_1_1.parameters[i].u.descriptor);
                 break;
             default:
-                FIXME("Unrecognized type %#x.\n", versioned_root_signature_get_parameter_type(desc, i));
                 vkd3d_shader_error(&context->message_context, NULL, VKD3D_SHADER_ERROR_RS_INVALID_ROOT_PARAMETER_TYPE,
                         "Invalid/unrecognised root signature root parameter type %#x.",
                         versioned_root_signature_get_parameter_type(desc, i));
@@ -1403,7 +1393,6 @@ static int validate_descriptor_table_v_1_0(const struct vkd3d_shader_root_descri
         }
         else
         {
-            WARN("Invalid descriptor range type %#x.\n", r->range_type);
             vkd3d_shader_error(message_context, NULL, VKD3D_SHADER_ERROR_RS_INVALID_DESCRIPTOR_RANGE_TYPE,
                     "Invalid root signature descriptor range type %#x.", r->range_type);
             return VKD3D_ERROR_INVALID_ARGUMENT;
@@ -1412,7 +1401,6 @@ static int validate_descriptor_table_v_1_0(const struct vkd3d_shader_root_descri
 
     if (have_srv_uav_cbv && have_sampler)
     {
-        WARN("Samplers cannot be mixed with CBVs/SRVs/UAVs in descriptor tables.\n");
         vkd3d_shader_error(message_context, NULL, VKD3D_SHADER_ERROR_RS_MIXED_DESCRIPTOR_RANGE_TYPES,
                 "Encountered both CBV/SRV/UAV and sampler descriptor ranges in the same root descriptor table.");
         return VKD3D_ERROR_INVALID_ARGUMENT;
@@ -1444,7 +1432,6 @@ static int validate_descriptor_table_v_1_1(const struct vkd3d_shader_root_descri
         }
         else
         {
-            WARN("Invalid descriptor range type %#x.\n", r->range_type);
             vkd3d_shader_error(message_context, NULL, VKD3D_SHADER_ERROR_RS_INVALID_DESCRIPTOR_RANGE_TYPE,
                     "Invalid root signature descriptor range type %#x.", r->range_type);
             return VKD3D_ERROR_INVALID_ARGUMENT;
@@ -1453,7 +1440,6 @@ static int validate_descriptor_table_v_1_1(const struct vkd3d_shader_root_descri
 
     if (have_srv_uav_cbv && have_sampler)
     {
-        WARN("Samplers cannot be mixed with CBVs/SRVs/UAVs in descriptor tables.\n");
         vkd3d_shader_error(message_context, NULL, VKD3D_SHADER_ERROR_RS_MIXED_DESCRIPTOR_RANGE_TYPES,
                 "Encountered both CBV/SRV/UAV and sampler descriptor ranges in the same root descriptor table.");
         return VKD3D_ERROR_INVALID_ARGUMENT;
@@ -1509,7 +1495,6 @@ int vkd3d_shader_serialize_root_signature(const struct vkd3d_shader_versioned_ro
             && root_signature->version != VKD3D_SHADER_ROOT_SIGNATURE_VERSION_1_1)
     {
         ret = VKD3D_ERROR_INVALID_ARGUMENT;
-        WARN("Root signature version %#x not supported.\n", root_signature->version);
         vkd3d_shader_error(&context.message_context, NULL, VKD3D_SHADER_ERROR_RS_INVALID_VERSION,
                 "Root signature version %#x is not supported.", root_signature->version);
         goto done;
diff --git a/libs/vkd3d/libs/vkd3d-shader/dxil.c b/libs/vkd3d/libs/vkd3d-shader/dxil.c
index 8e6c91f9cd7c..df398883e640 100644
--- a/libs/vkd3d/libs/vkd3d-shader/dxil.c
+++ b/libs/vkd3d/libs/vkd3d-shader/dxil.c
@@ -750,7 +750,6 @@ struct sm6_value
     const struct sm6_type *type;
     enum sm6_value_type value_type;
     unsigned int structure_stride;
-    bool is_back_ref;
     bool non_uniform;
     union
     {
@@ -868,7 +867,7 @@ struct sm6_descriptor_info
 {
     enum vkd3d_shader_descriptor_type type;
     unsigned int id;
-    struct vkd3d_shader_register_range range;
+    struct vsir_register_range range;
     enum dxil_resource_kind kind;
     enum vsir_data_type resource_data_type;
 };
@@ -911,6 +910,30 @@ struct dxil_attribute_group
     size_t attribute_capacity;
 };
 
+enum fixup_type
+{
+    FIXUP_LOAD,
+    FIXUP_STORE,
+    FIXUP_ATOMICRMW,
+    FIXUP_CMPXCHG,
+};
+
+struct fixup_data
+{
+    enum fixup_type type;
+    /* Points to the instruction just before the insertion place. */
+    struct vsir_program_iterator prev_it;
+    size_t value_idx;
+
+    const struct sm6_value *ptr;
+    const struct sm6_value *src;
+    const struct sm6_value *cmp;
+    const struct sm6_value *new;
+    unsigned int alignment;
+    enum vkd3d_shader_opcode op;
+    bool is_volatile;
+};
+
 struct sm6_parser
 {
     const uint32_t *ptr, *start, *end;
@@ -941,7 +964,6 @@ struct sm6_parser
     struct vsir_dst_operand *input_params;
     struct vsir_dst_operand *patch_constant_params;
     uint32_t io_regs_declared[VKD3D_BITMAP_SIZE(VKD3DSPR_COUNT)];
-    struct vsir_src_operand *outpointid_param;
 
     struct sm6_function *functions;
     size_t function_count;
@@ -971,6 +993,10 @@ struct sm6_parser
     size_t attribute_group_count;
 
     struct vkd3d_shader_parser p;
+
+    struct fixup_data *fixups;
+    size_t fixup_count;
+    size_t fixup_capacity;
 };
 
 struct dxil_abbrev_operand
@@ -1702,7 +1728,6 @@ static bool dxil_record_validate_operand_min_count(const struct dxil_record *rec
     if (record->operand_count >= min_count)
         return true;
 
-    WARN("Invalid operand count %u for code %u.\n", record->operand_count, record->code);
     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
             "Invalid operand count %u for record code %u.", record->operand_count, record->code);
     return false;
@@ -1714,7 +1739,6 @@ static void dxil_record_validate_operand_max_count(const struct dxil_record *rec
     if (record->operand_count <= max_count)
         return;
 
-    WARN("Ignoring %u extra operands for code %u.\n", record->operand_count - max_count, record->code);
     vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
             "Ignoring %u extra operands for record code %u.", record->operand_count - max_count, record->code);
 }
@@ -1795,7 +1819,7 @@ static enum vkd3d_result sm6_parser_type_table_init(struct sm6_parser *sm6)
                 break;
 
             case TYPE_CODE_DOUBLE:
-                sm6->program->f64_denorm_mode = VSIR_DENORM_PRESERVE;
+                sm6->program->f64_denormal_mode = VKD3D_SHADER_DENORMAL_MODE_PRESERVE;
                 dxil_record_validate_operand_max_count(record, 0, sm6);
                 type->class = TYPE_CLASS_FLOAT;
                 type->u.width = 64;
@@ -1844,6 +1868,7 @@ static enum vkd3d_result sm6_parser_type_table_init(struct sm6_parser *sm6)
                 break;
 
             case TYPE_CODE_HALF:
+                sm6->program->f16_denormal_mode = VKD3D_SHADER_DENORMAL_MODE_PRESERVE;
                 dxil_record_validate_operand_max_count(record, 0, sm6);
                 type->class = TYPE_CLASS_FLOAT;
                 type->u.width = 16;
@@ -2445,11 +2470,11 @@ static const struct sm6_type *sm6_parser_get_type(struct sm6_parser *sm6, uint64
 {
     if (type_id >= sm6->type_count)
     {
-        WARN("Invalid type index %"PRIu64" at %zu.\n", type_id, sm6->value_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE_ID,
                 "DXIL type id %"PRIu64" is invalid.", type_id);
         return NULL;
     }
+
     return &sm6->types[type_id];
 }
 
@@ -2786,7 +2811,6 @@ static struct vsir_src_operand *instruction_src_params_alloc(struct vkd3d_shader
 
     if (!(params = vsir_program_get_src_operands(sm6->program, count)))
     {
-        ERR("Failed to allocate src params.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
                 "Out of memory allocating instruction src parameters.");
         return NULL;
@@ -2813,10 +2837,10 @@ static struct vsir_dst_operand *instruction_dst_params_alloc(struct vkd3d_shader
     return dst;
 }
 
-static void register_init_with_id(struct vkd3d_shader_register *reg,
+static void vsir_operand_init_with_id(struct vsir_operand *reg,
         enum vkd3d_shader_register_type reg_type, enum vsir_data_type data_type, unsigned int id)
 {
-    vsir_register_init(reg, reg_type, data_type, 1);
+    vsir_operand_init(reg, reg_type, data_type, 1);
     reg->idx[0].offset = id;
 }
 
@@ -2867,49 +2891,7 @@ static enum vsir_data_type vsir_data_type_from_dxil(const struct sm6_type *type,
     return VSIR_DATA_U32;
 }
 
-/* Based on the implementation in the OpenGL Mathematics library. */
-static uint32_t half_to_float(uint16_t value)
-{
-    uint32_t s = (value & 0x8000u) << 16;
-    uint32_t e = (value >> 10) & 0x1fu;
-    uint32_t m = value & 0x3ffu;
-
-    if (!e)
-    {
-        if (!m)
-        {
-            /* Plus or minus zero */
-            return s;
-        }
-        else
-        {
-            /* Denormalized number -- renormalize it */
-            while (!(m & 0x400u))
-            {
-                m <<= 1;
-                --e;
-            }
-
-            ++e;
-            m &= ~0x400u;
-        }
-    }
-    else if (e == 31u)
-    {
-        /* Positive or negative infinity for zero 'm'.
-         * Nan for non-zero 'm' -- preserve sign and significand bits */
-        return s | 0x7f800000u | (m << 13);
-    }
-
-    /* Normalized number */
-    e += 127u - 15u;
-    m <<= 13;
-
-    /* Assemble s, e and m. */
-    return s | (e << 23) | m;
-}
-
-static void register_convert_to_minimum_precision(struct vkd3d_shader_register *reg)
+static void vsir_operand_convert_to_minimum_precision(struct vsir_operand *reg)
 {
     unsigned int i;
 
@@ -2921,7 +2903,9 @@ static void register_convert_to_minimum_precision(struct vkd3d_shader_register *
             if (reg->type == VKD3DSPR_IMMCONST)
             {
                 for (i = 0; i < VSIR_DIMENSION_VEC4; ++i)
-                    reg->u.immconst_u32[i] = half_to_float(reg->u.immconst_u32[i]);
+                {
+                    reg->u.immconst_u32[i] = vkd3d_f32_from_f16(reg->u.immconst_u32[i]);
+                }
             }
             break;
 
@@ -2950,10 +2934,10 @@ static void register_convert_to_minimum_precision(struct vkd3d_shader_register *
     }
 }
 
-static void register_index_address_init(struct vkd3d_shader_register_index *idx, const struct sm6_value *address,
-        struct sm6_parser *sm6);
+static void register_index_address_init(struct vsir_register_index *idx,
+        const struct sm6_value *address, struct sm6_parser *sm6);
 
-static void vsir_register_from_dxil_value(struct vkd3d_shader_register *reg,
+static void vsir_operand_from_dxil_value(struct vsir_operand *reg,
         const struct sm6_value *value, uint32_t type_flags, struct sm6_parser *dxil)
 {
     const struct sm6_type *scalar_type;
@@ -2966,26 +2950,26 @@ static void vsir_register_from_dxil_value(struct vkd3d_shader_register *reg,
     {
         case VALUE_TYPE_SSA:
             VKD3D_ASSERT(sm6_type_is_numeric(value->type) || sm6_type_is_struct(value->type));
-            register_init_with_id(reg, VKD3DSPR_SSA, data_type, value->u.ssa.id);
+            vsir_operand_init_with_id(reg, VKD3DSPR_SSA, data_type, value->u.ssa.id);
             reg->dimension = sm6_type_is_numeric(value->type) ? VSIR_DIMENSION_SCALAR : VSIR_DIMENSION_VEC4;
             break;
 
         case VALUE_TYPE_ICB:
-            vsir_register_init(reg, VKD3DSPR_IMMCONSTBUFFER, data_type, 2);
+            vsir_operand_init(reg, VKD3DSPR_IMMCONSTBUFFER, data_type, 2);
             reg->idx[0].offset = value->u.icb.id;
             register_index_address_init(&reg->idx[1], value->u.icb.index.index, dxil);
             reg->idx[1].is_in_bounds = value->u.icb.index.is_in_bounds;
             break;
 
         case VALUE_TYPE_IDXTEMP:
-            vsir_register_init(reg, VKD3DSPR_IDXTEMP, data_type, 2);
+            vsir_operand_init(reg, VKD3DSPR_IDXTEMP, data_type, 2);
             reg->idx[0].offset = value->u.idxtemp.id;
             register_index_address_init(&reg->idx[1], value->u.idxtemp.index.index, dxil);
             reg->idx[1].is_in_bounds = value->u.idxtemp.index.is_in_bounds;
             break;
 
         case VALUE_TYPE_GROUPSHAREDMEM:
-            vsir_register_init(reg, VKD3DSPR_GROUPSHAREDMEM, data_type, 2);
+            vsir_operand_init(reg, VKD3DSPR_GROUPSHAREDMEM, data_type, 2);
             reg->idx[0].offset = value->u.groupsharedmem.id;
             register_index_address_init(&reg->idx[1], value->u.groupsharedmem.index.index, dxil);
             reg->idx[1].is_in_bounds = value->u.groupsharedmem.index.is_in_bounds;
@@ -2993,7 +2977,7 @@ static void vsir_register_from_dxil_value(struct vkd3d_shader_register *reg,
 
         case VALUE_TYPE_CONSTANT:
             VKD3D_ASSERT(sm6_type_is_numeric(value->type) || sm6_type_is_struct(value->type));
-            vsir_register_init(reg, scalar_type->u.width == 64 ? VKD3DSPR_IMMCONST64 : VKD3DSPR_IMMCONST,
+            vsir_operand_init(reg, scalar_type->u.width == 64 ? VKD3DSPR_IMMCONST64 : VKD3DSPR_IMMCONST,
                     data_type, 0);
             reg->dimension = sm6_type_is_numeric(value->type) ? VSIR_DIMENSION_SCALAR : VSIR_DIMENSION_VEC4;
             reg->u = value->u.constant.immconst;
@@ -3001,7 +2985,7 @@ static void vsir_register_from_dxil_value(struct vkd3d_shader_register *reg,
 
         case VALUE_TYPE_UNDEFINED:
         case VALUE_TYPE_INVALID:
-            vsir_register_init(reg, VKD3DSPR_UNDEF, data_type, 0);
+            vsir_operand_init(reg, VKD3DSPR_UNDEF, data_type, 0);
             break;
 
         case VALUE_TYPE_FUNCTION:
@@ -3010,7 +2994,7 @@ static void vsir_register_from_dxil_value(struct vkd3d_shader_register *reg,
             vkd3d_unreachable();
     }
 
-    register_convert_to_minimum_precision(reg);
+    vsir_operand_convert_to_minimum_precision(reg);
     reg->non_uniform = value->non_uniform;
 }
 
@@ -3036,9 +3020,9 @@ static void sm6_parser_init_ssa_value(struct sm6_parser *sm6, struct sm6_value *
                 "The type of SSA value %u is neither numeric nor a structure.", id);
 }
 
-static void register_make_constant_uint(struct vkd3d_shader_register *reg, unsigned int value)
+static void vsir_operand_init_constant_u32(struct vsir_operand *reg, unsigned int value)
 {
-    vsir_register_init(reg, VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0);
+    vsir_operand_init(reg, VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0);
     reg->u.immconst_u32[0] = value;
 }
 
@@ -3094,11 +3078,10 @@ static void src_param_init_from_value(struct vsir_src_operand *param,
         const struct sm6_value *src, uint32_t type_flags, struct sm6_parser *dxil)
 {
     src_param_init(param);
-    vsir_register_from_dxil_value(&param->reg, src, type_flags, dxil);
+    vsir_operand_from_dxil_value(&param->reg, src, type_flags, dxil);
 }
 
-static void src_param_init_vector_from_reg(struct vsir_src_operand *param,
-        const struct vkd3d_shader_register *reg)
+static void src_param_init_vector_from_reg(struct vsir_src_operand *param, const struct vsir_operand *reg)
 {
     param->swizzle = (reg->dimension == VSIR_DIMENSION_VEC4) ? VKD3D_SHADER_NO_SWIZZLE : VKD3D_SHADER_SWIZZLE(X, X, X, X);
     param->modifiers = VKD3DSPSM_NONE;
@@ -3108,10 +3091,10 @@ static void src_param_init_vector_from_reg(struct vsir_src_operand *param,
 static void src_param_make_constant_uint(struct vsir_src_operand *param, unsigned int value)
 {
     src_param_init(param);
-    register_make_constant_uint(&param->reg, value);
+    vsir_operand_init_constant_u32(&param->reg, value);
 }
 
-static void register_index_address_init(struct vkd3d_shader_register_index *idx,
+static void register_index_address_init(struct vsir_register_index *idx,
         const struct sm6_value *address, struct sm6_parser *sm6)
 {
     struct vsir_src_operand *rel_addr;
@@ -3136,7 +3119,7 @@ static void register_index_address_init(struct vkd3d_shader_register_index *idx,
 }
 
 static void sm6_register_from_handle(struct sm6_parser *sm6,
-        const struct sm6_handle_data *handle, struct vkd3d_shader_register *reg)
+        const struct sm6_handle_data *handle, struct vsir_operand *reg)
 {
     enum vkd3d_shader_register_type reg_type;
     enum vsir_data_type data_type;
@@ -3167,7 +3150,7 @@ static void sm6_register_from_handle(struct sm6_parser *sm6,
             vkd3d_unreachable();
     }
 
-    vsir_register_init(reg, reg_type, data_type, 2);
+    vsir_operand_init(reg, reg_type, data_type, 2);
     reg->dimension = VSIR_DIMENSION_VEC4;
     reg->idx[0].offset = handle->d->id;
     register_index_address_init(&reg->idx[1], handle->index, sm6);
@@ -3177,7 +3160,7 @@ static void sm6_register_from_handle(struct sm6_parser *sm6,
 static void src_param_init_vector_from_handle(struct sm6_parser *sm6,
         struct vsir_src_operand *param, const struct sm6_handle_data *handle)
 {
-    struct vkd3d_shader_register reg;
+    struct vsir_operand reg;
 
     sm6_register_from_handle(sm6, handle, &reg);
     src_param_init_vector_from_reg(param, &reg);
@@ -3194,7 +3177,7 @@ static bool instruction_dst_param_init_ssa_scalar(struct vkd3d_shader_instructio
 
     dst_param_init(param);
     sm6_parser_init_ssa_value(dxil, dst);
-    vsir_register_from_dxil_value(&param->reg, dst, type_flags, dxil);
+    vsir_operand_from_dxil_value(&param->reg, dst, type_flags, dxil);
 
     return true;
 }
@@ -3210,7 +3193,7 @@ static bool instruction_dst_param_init_ssa_vector(struct vkd3d_shader_instructio
 
     dst_param_init_vector(vsir_dst, component_count);
     sm6_parser_init_ssa_value(dxil, dxil_dst);
-    vsir_register_from_dxil_value(&vsir_dst->reg, dxil_dst, 0, dxil);
+    vsir_operand_from_dxil_value(&vsir_dst->reg, dxil_dst, 0, dxil);
 
     return true;
 }
@@ -3295,7 +3278,6 @@ static bool sm6_value_validate_is_register(const struct sm6_value *value, struct
 {
     if (!sm6_value_is_register(value))
     {
-        WARN("Operand of type %u is not a register.\n", value->value_type);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "A register operand passed to a DXIL instruction is not a register.");
         return false;
@@ -3307,7 +3289,6 @@ static bool sm6_value_validate_is_handle(const struct sm6_value *value, struct s
 {
     if (!sm6_value_is_handle(value))
     {
-        WARN("Handle parameter of type %u is not a handle.\n", value->value_type);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCE_HANDLE,
                 "A handle parameter passed to a DX intrinsic function is not a handle.");
         return false;
@@ -3326,7 +3307,6 @@ static bool sm6_value_validate_is_texture_handle(const struct sm6_value *value,
     kind = value->u.handle.d->kind;
     if (!resource_kind_is_texture(kind))
     {
-        WARN("Resource kind %u for op %u is not a texture.\n", kind, op);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCE_HANDLE,
                 "Resource kind %u for texture operation %u is not a texture.", kind, op);
         return false;
@@ -3346,7 +3326,6 @@ static bool sm6_value_validate_is_texture_2dms_handle(const struct sm6_value *va
     kind = value->u.handle.d->kind;
     if (!resource_kind_is_multisampled(kind))
     {
-        WARN("Resource kind %u for op %u is not a 2DMS texture.\n", kind, op);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCE_HANDLE,
                 "Resource kind %u for texture operation %u is not a 2DMS texture.", kind, op);
         return false;
@@ -3366,7 +3345,6 @@ static bool sm6_value_validate_is_sampler_handle(const struct sm6_value *value,
     kind = value->u.handle.d->kind;
     if (kind != RESOURCE_KIND_SAMPLER)
     {
-        WARN("Resource kind %u for op %u is not a sampler.\n", kind, op);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCE_HANDLE,
                 "Resource kind %u for sample operation %u is not a sampler.", kind, op);
         return false;
@@ -3379,7 +3357,6 @@ static bool sm6_value_validate_is_pointer(const struct sm6_value *value, struct
 {
     if (!sm6_type_is_pointer(value->type))
     {
-        WARN("Operand result type class %u is not a pointer.\n", value->type->class);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "A pointer operand passed to a DXIL instruction is not a pointer.");
         return false;
@@ -3387,22 +3364,10 @@ static bool sm6_value_validate_is_pointer(const struct sm6_value *value, struct
     return true;
 }
 
-static bool sm6_value_validate_is_backward_ref(const struct sm6_value *value, struct sm6_parser *sm6)
-{
-    if (!value->is_back_ref)
-    {
-        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_NOT_IMPLEMENTED,
-                "Forward-referenced pointer declarations are not supported.");
-        return false;
-    }
-    return true;
-}
-
 static bool sm6_value_validate_is_numeric(const struct sm6_value *value, struct sm6_parser *sm6)
 {
     if (!sm6_type_is_numeric(value->type))
     {
-        WARN("Operand result type class %u is not numeric.\n", value->type->class);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "A numeric operand passed to a DXIL instruction is not numeric.");
         return false;
@@ -3415,7 +3380,6 @@ static bool sm6_value_validate_is_bool(const struct sm6_value *value, struct sm6
     const struct sm6_type *type = value->type;
     if (!sm6_type_is_bool(type))
     {
-        WARN("Operand of type class %u is not bool.\n", type->class);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "A bool operand of type class %u passed to a DXIL instruction is not a bool.", type->class);
         return false;
@@ -3427,7 +3391,6 @@ static bool sm6_value_validate_is_pointer_to_i32(const struct sm6_value *value,
 {
     if (!sm6_type_is_pointer(value->type) || !sm6_type_is_i32(value->type->u.pointer.type))
     {
-        WARN("Operand result type %u is not a pointer to i32.\n", value->type->class);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "An int32 pointer operand passed to a DXIL instruction is not an int32 pointer.");
         return false;
@@ -3439,7 +3402,6 @@ static bool sm6_value_validate_is_i32(const struct sm6_value *value, struct sm6_
 {
     if (!sm6_type_is_i32(value->type))
     {
-        WARN("Operand result type %u is not i32.\n", value->type->class);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "An int32 operand passed to a DXIL instruction is not an int32.");
         return false;
@@ -3452,14 +3414,13 @@ static struct sm6_value *sm6_parser_get_value_safe(struct sm6_parser *sm6, unsig
     if (idx < sm6->value_count)
         return &sm6->values[idx];
 
-    WARN("Invalid value index %u.\n", idx);
     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
             "Invalid value index %u.", idx);
     return NULL;
 }
 
 static void sm6_parser_pre_init_or_validate_referenced_value(struct sm6_parser *dxil,
-        size_t operand, const struct sm6_type *fwd_type)
+        size_t operand, const struct sm6_type *fwd_type, bool expect_ssa)
 {
     struct sm6_value *value;
 
@@ -3475,7 +3436,7 @@ static void sm6_parser_pre_init_or_validate_referenced_value(struct sm6_parser *
             vkd3d_shader_parser_warning(&dxil->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH,
                     "The type of a source value does not match the predefined type.");
     }
-    else
+    else if (expect_ssa)
     {
         value->type = fwd_type;
         value->value_type = VALUE_TYPE_SSA;
@@ -3485,10 +3446,18 @@ static void sm6_parser_pre_init_or_validate_referenced_value(struct sm6_parser *
             vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE,
                     "The type of SSA value %u is neither numeric nor a structure.", value->u.ssa.id);
     }
+    else
+    {
+        value->type = fwd_type;
+
+        if (!sm6_type_is_pointer(value->type))
+            vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE,
+                    "A pointer forward reference doesn't have a pointer type.");
+    }
 }
 
 static size_t sm6_parser_get_value_idx_by_ref(struct sm6_parser *sm6, const struct dxil_record *record,
-        const struct sm6_type *fwd_type, unsigned int *rec_idx)
+        const struct sm6_type *fwd_type, unsigned int *rec_idx, bool expect_ssa)
 {
     unsigned int idx;
     uint64_t val_ref;
@@ -3520,7 +3489,7 @@ static size_t sm6_parser_get_value_idx_by_ref(struct sm6_parser *sm6, const stru
     *rec_idx = idx;
 
     if (fwd_type)
-        sm6_parser_pre_init_or_validate_referenced_value(sm6, operand, fwd_type);
+        sm6_parser_pre_init_or_validate_referenced_value(sm6, operand, fwd_type, expect_ssa);
 
     return operand;
 }
@@ -3551,16 +3520,25 @@ static const struct sm6_value *sm6_parser_get_value_by_rotated_signed_idx(struct
     if ((operand = sm6_parser_get_value_index(dxil, rotated_idx)) == SIZE_MAX)
         return NULL;
 
-    sm6_parser_pre_init_or_validate_referenced_value(dxil, operand, fwd_type);
+    sm6_parser_pre_init_or_validate_referenced_value(dxil, operand, fwd_type, true);
 
     return &dxil->values[operand];
 }
 
-static const struct sm6_value *sm6_parser_get_value_by_ref(struct sm6_parser *sm6,
+static const struct sm6_value *sm6_parser_get_value_by_ref(struct sm6_parser *dxil,
+        const struct dxil_record *record, const struct sm6_type *type, unsigned int *rec_idx)
+{
+    size_t operand = sm6_parser_get_value_idx_by_ref(dxil, record, type, rec_idx, true);
+
+    return operand == SIZE_MAX ? NULL : &dxil->values[operand];
+}
+
+static const struct sm6_value *sm6_parser_get_pointer_value_by_ref(struct sm6_parser *dxil,
         const struct dxil_record *record, const struct sm6_type *type, unsigned int *rec_idx)
 {
-    size_t operand = sm6_parser_get_value_idx_by_ref(sm6, record, type, rec_idx);
-    return operand == SIZE_MAX ? NULL : &sm6->values[operand];
+    size_t operand = sm6_parser_get_value_idx_by_ref(dxil, record, type, rec_idx, false);
+
+    return operand == SIZE_MAX ? NULL : &dxil->values[operand];
 }
 
 static bool sm6_parser_declare_function(struct sm6_parser *sm6, const struct dxil_record *record)
@@ -3640,7 +3618,6 @@ static struct sm6_index *sm6_get_value_index(struct sm6_parser *sm6, struct sm6_
             return &value->u.groupsharedmem.index;
 
         default:
-            WARN("Cannot index into value of type %#x.\n", value->value_type);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                     "Cannot index into value of type %#x.", value->value_type);
             return NULL;
@@ -3669,14 +3646,12 @@ static enum vkd3d_result sm6_parser_init_constexpr_gep(struct sm6_parser *sm6, c
 
         if ((value = record->operands[i + 1]) >= sm6->cur_max_value)
         {
-            WARN("Invalid value index %"PRIu64".\n", value);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                     "Invalid value index %"PRIu64".", value);
             return VKD3D_ERROR_INVALID_SHADER;
         }
         else if (value == sm6->value_count)
         {
-            WARN("Invalid value self-reference at %"PRIu64".\n", value);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                     "Invalid value self-reference for a constexpr GEP.");
             return VKD3D_ERROR_INVALID_SHADER;
@@ -3684,15 +3659,10 @@ static enum vkd3d_result sm6_parser_init_constexpr_gep(struct sm6_parser *sm6, c
 
         operands[j] = &sm6->values[value];
         if (value > sm6->value_count)
-        {
             operands[j]->type = elem_type;
-        }
         else if (operands[j]->type != elem_type)
-        {
-            WARN("Type mismatch.\n");
             vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH,
                     "Type mismatch in constexpr GEP elements.");
-        }
     }
 
     *dst = *operands[0];
@@ -3703,7 +3673,6 @@ static enum vkd3d_result sm6_parser_init_constexpr_gep(struct sm6_parser *sm6, c
 
     if (index->index)
     {
-        WARN("Unsupported stacked GEP.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "A GEP instruction on the result of a previous GEP is unsupported.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -3711,14 +3680,12 @@ static enum vkd3d_result sm6_parser_init_constexpr_gep(struct sm6_parser *sm6, c
 
     if (!sm6_value_is_constant_zero(operands[1]))
     {
-        WARN("Expected constant zero.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "The pointer dereference index for a constexpr GEP instruction is not constant zero.");
         return VKD3D_ERROR_INVALID_SHADER;
     }
     if (!sm6_value_is_constant(operands[2]) || !sm6_type_is_integer(operands[2]->type))
     {
-        WARN("Element index is not constant int.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "A constexpr GEP element index is not a constant integer.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -3727,27 +3694,20 @@ static enum vkd3d_result sm6_parser_init_constexpr_gep(struct sm6_parser *sm6, c
     ptr_type = operands[0]->type;
     if (!sm6_type_is_pointer(ptr_type))
     {
-        WARN("Constexpr GEP base value is not a pointer.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "A constexpr GEP base value is not a pointer.");
         return VKD3D_ERROR_INVALID_SHADER;
     }
 
     if (!pointee_type)
-    {
         pointee_type = ptr_type->u.pointer.type;
-    }
     else if (pointee_type != ptr_type->u.pointer.type)
-    {
-        WARN("Explicit pointee type mismatch.\n");
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH,
                 "Explicit pointee type for constexpr GEP does not match the element type.");
-    }
 
     offset = sm6_value_get_constant_uint(operands[2], sm6);
     if (!(gep_type = sm6_type_get_element_type_at_index(pointee_type, offset)))
     {
-        WARN("Failed to get element type.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "Failed to get the element type of a constexpr GEP.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -3755,7 +3715,6 @@ static enum vkd3d_result sm6_parser_init_constexpr_gep(struct sm6_parser *sm6, c
 
     if (!(dst->type = sm6_type_get_pointer_to_type(gep_type, ptr_type->u.pointer.addr_space, sm6)))
     {
-        WARN("Failed to get pointer type for type %u.\n", gep_type->class);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE,
                 "Module does not define a pointer type for a constexpr GEP result.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -3805,7 +3764,6 @@ static enum vkd3d_result sm6_parser_constants_init(struct sm6_parser *sm6, const
 
         dst = sm6_parser_get_current_value(sm6);
         dst->type = type;
-        dst->is_back_ref = true;
 
         switch (record->code)
         {
@@ -3999,7 +3957,6 @@ static enum vkd3d_result sm6_parser_constants_init(struct sm6_parser *sm6, const
 
                 if ((value = record->operands[0]) != CAST_BITCAST)
                 {
-                    WARN("Unhandled constexpr cast op %"PRIu64".\n", value);
                     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                             "Constexpr cast op %"PRIu64" is unhandled.", value);
                     return VKD3D_ERROR_INVALID_SHADER;
@@ -4008,7 +3965,6 @@ static enum vkd3d_result sm6_parser_constants_init(struct sm6_parser *sm6, const
                 ptr_type = sm6_parser_get_type(sm6, record->operands[1]);
                 if (!sm6_type_is_pointer(ptr_type))
                 {
-                    WARN("Constexpr cast at constant idx %zu is not a pointer.\n", value_idx);
                     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                             "Constexpr cast source operand is not a pointer.");
                     return VKD3D_ERROR_INVALID_SHADER;
@@ -4016,14 +3972,12 @@ static enum vkd3d_result sm6_parser_constants_init(struct sm6_parser *sm6, const
 
                 if ((value = record->operands[2]) >= sm6->cur_max_value)
                 {
-                    WARN("Invalid value index %"PRIu64".\n", value);
                     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                             "Invalid value index %"PRIu64".", value);
                     return VKD3D_ERROR_INVALID_SHADER;
                 }
                 else if (value == value_idx)
                 {
-                    WARN("Invalid value self-reference at %"PRIu64".\n", value);
                     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                             "Invalid value self-reference for a constexpr cast.");
                     return VKD3D_ERROR_INVALID_SHADER;
@@ -4099,7 +4053,7 @@ static void sm6_parser_declare_indexable_temp(struct sm6_parser *sm6, const stru
 {
     enum vsir_data_type data_type = vsir_data_type_from_dxil(elem_type, 0, sm6);
 
-    if (!(sm6->program->global_flags & VKD3DSGF_FORCE_NATIVE_LOW_PRECISION))
+    if (!(sm6->program->global_flags & VKD3DSGF_ENABLE_NATIVE_LOW_PRECISION))
     {
         if (data_type == VSIR_DATA_F16)
             data_type = VSIR_DATA_F32;
@@ -4138,7 +4092,7 @@ static void sm6_parser_declare_tgsm_raw(struct sm6_parser *sm6, const struct sm6
     dst->value_type = VALUE_TYPE_GROUPSHAREDMEM;
     dst->u.groupsharedmem.id = sm6->tgsm_count++;
     dst->structure_stride = 0;
-    vsir_register_from_dxil_value(&ins->declaration.tgsm_raw.reg.reg, dst, 0, sm6);
+    vsir_operand_from_dxil_value(&ins->declaration.tgsm_raw.reg.reg, dst, 0, sm6);
     ins->declaration.tgsm_raw.alignment = alignment;
     byte_count = elem_type->u.width / CHAR_BIT;
     /* Convert minimum precision types to their 32-bit equivalent. */
@@ -4166,7 +4120,7 @@ static void sm6_parser_declare_tgsm_structured(struct sm6_parser *sm6, const str
     /* Convert minimum precision types to their 32-bit equivalent. */
     if (dst->structure_stride == 2)
         dst->structure_stride = 4;
-    vsir_register_from_dxil_value(&ins->declaration.tgsm_structured.reg.reg, dst, 0, sm6);
+    vsir_operand_from_dxil_value(&ins->declaration.tgsm_structured.reg.reg, dst, 0, sm6);
     if (dst->structure_stride != 4)
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_NOT_IMPLEMENTED,
                 "Structured TGSM byte stride %u is not supported.", dst->structure_stride);
@@ -4222,8 +4176,6 @@ static bool sm6_parser_declare_global(struct sm6_parser *sm6, const struct dxil_
 
         if (!(type = sm6_type_get_pointer_to_type(type, address_space, sm6)))
         {
-            WARN("Failed to get pointer type for type class %u, address space %"PRIu64".\n",
-                    type->class, address_space);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE,
                     "Module does not define a pointer type for a global variable.");
             return false;
@@ -4233,7 +4185,6 @@ static bool sm6_parser_declare_global(struct sm6_parser *sm6, const struct dxil_
     {
         if (!sm6_type_is_pointer(type))
         {
-            WARN("Type is not a pointer.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                     "The type of a global variable is not a pointer.");
             return false;
@@ -4245,7 +4196,6 @@ static bool sm6_parser_declare_global(struct sm6_parser *sm6, const struct dxil_
     {
         if (init - 1 >= sm6->value_capacity)
         {
-            WARN("Invalid value index %"PRIu64" for initialiser.", init - 1);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                     "Global variable initialiser value index %"PRIu64" is invalid.", init - 1);
             return false;
@@ -4281,11 +4231,9 @@ static bool sm6_parser_declare_global(struct sm6_parser *sm6, const struct dxil_
 
     dst = sm6_parser_get_current_value(sm6);
     dst->type = type;
-    dst->is_back_ref = true;
 
     if (is_constant && !init)
     {
-        WARN("Constant array has no initialiser.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "A constant global variable has no initialiser.");
         return false;
@@ -4302,7 +4250,6 @@ static bool sm6_parser_declare_global(struct sm6_parser *sm6, const struct dxil_
     {
         if (!sm6_type_is_numeric(scalar_type))
         {
-            WARN("Unsupported type class %u.\n", scalar_type->class);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                     "TGSM variables of type class %u are not supported.", scalar_type->class);
             return false;
@@ -4333,7 +4280,6 @@ static const struct vkd3d_shader_immediate_constant_buffer *resolve_forward_init
     if (!(value = sm6_parser_get_value_safe(sm6, index))
             || (!sm6_value_is_constant_array(value) && !sm6_value_is_undef(value)))
     {
-        WARN("Invalid initialiser index %zu.\n", index);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "Global variable initialiser value index %zu is invalid.", index);
         return NULL;
@@ -4363,7 +4309,6 @@ static const struct vkd3d_shader_immediate_constant_buffer *resolve_forward_init
         /* Arrays of bool are not used in DXIL. dxc will emit an array of int32 instead if necessary. */
         if (!(size = elem_type->u.width / CHAR_BIT))
         {
-            WARN("Invalid data type width %u.\n", elem_type->u.width);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                     "An immediate constant buffer is declared with boolean elements.");
             return NULL;
@@ -4403,7 +4348,7 @@ static const struct vkd3d_shader_immediate_constant_buffer *resolve_forward_init
                 icb->data_type = VSIR_DATA_F32;
                 for (i = 0; i < count; ++i)
                 {
-                    icb->data[i] = half_to_float(elements[i]);
+                    icb->data[i] = vkd3d_f32_from_f16(elements[i]);
                 }
                 break;
 
@@ -4465,7 +4410,6 @@ static bool resolve_forward_zero_initialiser(size_t index, struct sm6_parser *sm
     if (!(value = sm6_parser_get_value_safe(sm6, index)) || (!sm6_value_is_constant_array(value)
             && !sm6_value_is_constant(value) && !sm6_value_is_undef(value)))
     {
-        WARN("Invalid initialiser index %zu.\n", index);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "TGSM initialiser value index %zu is invalid.", index);
         return false;
@@ -4595,7 +4539,7 @@ static void dst_param_io_init(struct vsir_dst_operand *param, const struct signa
     param->shift = 0;
     /* DXIL types do not have signedness. Load signed elements as unsigned. */
     component_type = e->component_type == VKD3D_SHADER_COMPONENT_INT ? VKD3D_SHADER_COMPONENT_UINT : e->component_type;
-    vsir_register_init(&param->reg, reg_type, vsir_data_type_from_component_type(component_type), 0);
+    vsir_operand_init(&param->reg, reg_type, vsir_data_type_from_component_type(component_type), 0);
     param->reg.dimension = dimension;
 }
 
@@ -4636,12 +4580,15 @@ static enum vkd3d_shader_register_type register_type_from_dxil_semantic_kind(
         case VKD3D_SHADER_SV_DEPTH_LESS_EQUAL:
             *dimension = VSIR_DIMENSION_SCALAR;
             return VKD3DSPR_DEPTHOUTLE;
+        case VKD3D_SHADER_SV_STENCIL_REF:
+            *dimension = VSIR_DIMENSION_SCALAR;
+            return VKD3DSPR_OUTSTENCILREF;
         default:
             return VKD3DSPR_INVALID;
     }
 }
 
-static void sm6_parser_init_signature(struct sm6_parser *sm6, const struct shader_signature *s,
+static bool sm6_parser_init_signature(struct sm6_parser *sm6, const struct shader_signature *s,
         bool is_input, enum vkd3d_shader_register_type reg_type, struct vsir_dst_operand *params)
 {
     enum vkd3d_shader_type shader_type = sm6->program->shader_version.type;
@@ -4695,8 +4642,8 @@ static void sm6_parser_init_signature(struct sm6_parser *sm6, const struct shade
         {
             if (reg_type == VKD3DSPR_OUTPUT)
             {
-                VKD3D_ASSERT(sm6->outpointid_param);
-                param->reg.idx[count].rel_addr = sm6->outpointid_param;
+                if (!(param->reg.idx[count].rel_addr = vsir_program_create_outpointid_param(sm6->program)))
+                    return false;
             }
             param->reg.idx[count++].offset = 0;
         }
@@ -4708,6 +4655,8 @@ static void sm6_parser_init_signature(struct sm6_parser *sm6, const struct shade
         param->reg.idx[count++].offset = i;
         param->reg.idx_count = count;
     }
+
+    return true;
 }
 
 static int sm6_parser_init_output_signature(struct sm6_parser *sm6, const struct shader_signature *output_signature)
@@ -4719,7 +4668,8 @@ static int sm6_parser_init_output_signature(struct sm6_parser *sm6, const struct
         return VKD3D_ERROR_OUT_OF_MEMORY;
     }
 
-    sm6_parser_init_signature(sm6, output_signature, false, VKD3DSPR_OUTPUT, sm6->output_params);
+    if (!sm6_parser_init_signature(sm6, output_signature, false, VKD3DSPR_OUTPUT, sm6->output_params))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
 
     return VKD3D_OK;
 }
@@ -4733,7 +4683,8 @@ static int sm6_parser_init_input_signature(struct sm6_parser *sm6, const struct
         return VKD3D_ERROR_OUT_OF_MEMORY;
     }
 
-    sm6_parser_init_signature(sm6, input_signature, true, VKD3DSPR_INPUT, sm6->input_params);
+    if (!sm6_parser_init_signature(sm6, input_signature, true, VKD3DSPR_INPUT, sm6->input_params))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
 
     return VKD3D_OK;
 }
@@ -4751,8 +4702,9 @@ static int sm6_parser_init_patch_constant_signature(struct sm6_parser *sm6,
         return VKD3D_ERROR_OUT_OF_MEMORY;
     }
 
-    sm6_parser_init_signature(sm6, patch_constant_signature, is_input, VKD3DSPR_PATCHCONST,
-            sm6->patch_constant_params);
+    if (!sm6_parser_init_signature(sm6, patch_constant_signature, is_input,
+            VKD3DSPR_PATCHCONST, sm6->patch_constant_params))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
 
     return VKD3D_OK;
 }
@@ -4777,6 +4729,7 @@ struct function_emission_state
 {
     struct sm6_function *function;
     const struct dxil_record *record;
+    struct fixup_data *fixup;
     unsigned int temp_idx;
 };
 
@@ -4784,18 +4737,105 @@ static struct vkd3d_shader_instruction *sm6_parser_add_function_instruction(stru
         struct function_emission_state *state)
 {
     struct sm6_function *function = state->function;
+    struct fixup_data *fixup = state->fixup;
     struct vkd3d_shader_instruction *ins;
 
-    if (!(ins = shader_instruction_array_append(&function->instructions)))
-        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
-                "Out of memory allocating instruction.");
+    if (fixup)
+    {
+        if (!vsir_program_iterator_insert_after(&fixup->prev_it, 1))
+        {
+            vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
+                    "Out of memory allocating fixup instruction.");
+            return NULL;
+        }
+
+        ins = vsir_program_iterator_next(&fixup->prev_it);
+        VKD3D_ASSERT(ins);
+    }
+    else
+    {
+        if (!(ins = shader_instruction_array_append(&function->instructions)))
+            vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
+                    "Out of memory allocating instruction.");
+    }
 
     return ins;
 }
 
+static struct fixup_data *sm6_parser_add_fixup(struct sm6_parser *dxil, struct function_emission_state *state,
+        enum fixup_type type)
+{
+    struct fixup_data *fixup;
+
+    if (!vkd3d_array_reserve((void **)&dxil->fixups, &dxil->fixup_capacity,
+            dxil->fixup_count + 1, sizeof(*dxil->fixups)))
+    {
+        vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
+                "Out of memory allocating a fixup.");
+        return NULL;
+    }
+
+    fixup = &dxil->fixups[dxil->fixup_count++];
+
+    fixup->type = type;
+    fixup->prev_it = vsir_program_iterator(&state->function->instructions);
+    vsir_program_iterator_tail(&fixup->prev_it);
+    fixup->value_idx = dxil->value_count;
+
+    return fixup;
+}
+
+static void sm6_parser_fixup_load(struct sm6_parser *dxil, struct function_emission_state *state);
+static void sm6_parser_fixup_store(struct sm6_parser *dxil, struct function_emission_state *state);
+static void sm6_parser_fixup_atomicrmw(struct sm6_parser *dxil, struct function_emission_state *state);
+static void sm6_parser_fixup_cmpxchg(struct sm6_parser *dxil, struct function_emission_state *state);
+
+static void sm6_parser_apply_fixups(struct sm6_parser *dxil, struct sm6_function *function)
+{
+    size_t i, prev_value_count = dxil->value_count;
+
+    /* Apply the fixups in reversed order, otherwise the first ones invalidate
+     * iterators for the later ones. */
+
+    for (i = dxil->fixup_count - 1; i != SIZE_MAX; --i)
+    {
+        struct fixup_data *fixup = &dxil->fixups[i];
+        struct function_emission_state state = {0};
+
+        state.fixup = fixup;
+        state.function = function;
+
+        dxil->value_count = fixup->value_idx;
+
+        switch (fixup->type)
+        {
+            case FIXUP_LOAD:
+                sm6_parser_fixup_load(dxil, &state);
+                break;
+
+            case FIXUP_STORE:
+                sm6_parser_fixup_store(dxil, &state);
+                break;
+
+            case FIXUP_ATOMICRMW:
+                sm6_parser_fixup_atomicrmw(dxil, &state);
+                break;
+
+            case FIXUP_CMPXCHG:
+                sm6_parser_fixup_cmpxchg(dxil, &state);
+                break;
+        }
+
+        dxil->program->temp_count = max(dxil->program->temp_count, state.temp_idx);
+    }
+
+    dxil->value_count = prev_value_count;
+    dxil->fixup_count = 0;
+}
+
 static bool sm6_parser_emit_reg_composite_construct(struct sm6_parser *sm6,
-        const struct vkd3d_shader_register *operand_regs, unsigned int component_count,
-        struct function_emission_state *state, struct vkd3d_shader_register *reg);
+        const struct vsir_operand *operand_regs, unsigned int component_count,
+        struct function_emission_state *state, struct vsir_operand *reg);
 
 static void sm6_parser_emit_alloca(struct sm6_parser *sm6, struct function_emission_state *state)
 {
@@ -4829,7 +4869,6 @@ static void sm6_parser_emit_alloca(struct sm6_parser *sm6, struct function_emiss
 
     if (!sm6_type_is_array(type[0]) || !sm6_type_is_numeric(elem_type = type[0]->u.array.elem_type))
     {
-        WARN("Type is not a numeric array.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "Result type of an ALLOCA instruction is not a numeric array.");
         return;
@@ -4838,7 +4877,6 @@ static void sm6_parser_emit_alloca(struct sm6_parser *sm6, struct function_emiss
      * forward reference. We only support a constant size, so no forward ref support is needed. */
     if (!sm6_type_is_integer(type[1]))
     {
-        WARN("Size operand type is not scalar integer.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "The type of the allocation size operand of an ALLOCA instruction is not scalar integer.");
         return;
@@ -4846,7 +4884,6 @@ static void sm6_parser_emit_alloca(struct sm6_parser *sm6, struct function_emiss
 
     if (!(dst->type = sm6_type_get_pointer_to_type(type[0], ADDRESS_SPACE_DEFAULT, sm6)))
     {
-        WARN("Failed to get pointer type for type class %u.\n", type[0]->class);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE,
                 "Module does not define a pointer type for an ALLOCA instruction.");
         return;
@@ -4903,47 +4940,32 @@ static enum vkd3d_shader_opcode map_dx_atomicrmw_op(uint64_t code)
     }
 }
 
-static void sm6_parser_emit_atomicrmw(struct sm6_parser *sm6, struct function_emission_state *state)
+static void sm6_parser_emit_atomicrmw(struct sm6_parser *dxil, struct function_emission_state *state)
 {
-    struct sm6_value *dst = sm6_parser_get_current_value(sm6);
+    struct sm6_value *dst = sm6_parser_get_current_value(dxil);
     const struct dxil_record *record = state->record;
-    struct vkd3d_shader_register regs[2], reg;
-    struct vkd3d_shader_instruction *ins;
-    struct vsir_src_operand *src_params;
-    struct vsir_dst_operand *dst_params;
-    struct vkd3d_shader_register coord;
     const struct sm6_value *ptr, *src;
     enum vkd3d_shader_opcode op;
+    struct fixup_data *fixup;
     unsigned int i = 0;
     bool is_volatile;
     uint64_t code;
 
-    if (!(ptr = sm6_parser_get_value_by_ref(sm6, record, NULL, &i))
-            || !sm6_value_validate_is_pointer_to_i32(ptr, sm6)
-            || !sm6_value_validate_is_backward_ref(ptr, sm6))
-        return;
-
-    vsir_register_from_dxil_value(&reg, ptr, 0, sm6);
-
-    if (reg.type != VKD3DSPR_GROUPSHAREDMEM)
-    {
-        WARN("Register is not groupshared.\n");
-        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
-                "The destination register for an atomicrmw instruction is not groupshared memory.");
+    if (!(ptr = sm6_parser_get_pointer_value_by_ref(dxil, record, NULL, &i))
+            || !sm6_value_validate_is_pointer_to_i32(ptr, dxil))
         return;
-    }
 
     dst->type = ptr->type->u.pointer.type;
 
-    if (!(src = sm6_parser_get_value_by_ref(sm6, record, dst->type, &i)))
+    if (!(src = sm6_parser_get_value_by_ref(dxil, record, dst->type, &i)))
         return;
 
-    if (!dxil_record_validate_operand_count(record, i + 4, i + 4, sm6))
+    if (!dxil_record_validate_operand_count(record, i + 4, i + 4, dxil))
         return;
 
     if ((op = map_dx_atomicrmw_op(code = record->operands[i++])) == VSIR_OP_INVALID)
     {
-        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_NOT_IMPLEMENTED,
+        vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_NOT_IMPLEMENTED,
                 "Operation %"PRIu64" for an atomicrmw instruction is unhandled.", code);
         return;
     }
@@ -4959,24 +4981,57 @@ static void sm6_parser_emit_atomicrmw(struct sm6_parser *sm6, struct function_em
     if ((code = record->operands[i]) != 1)
         WARN("Ignoring synchronisation scope %"PRIu64".\n", code);
 
+    sm6_parser_init_ssa_value(dxil, dst);
+
+    if (!(fixup = sm6_parser_add_fixup(dxil, state, FIXUP_ATOMICRMW)))
+        return;
+
+    fixup->ptr = ptr;
+    fixup->src = src;
+    fixup->op = op;
+    fixup->is_volatile = is_volatile;
+}
+
+static void sm6_parser_fixup_atomicrmw(struct sm6_parser *dxil, struct function_emission_state *state)
+{
+    struct sm6_value *dst = sm6_parser_get_current_value(dxil);
+    struct fixup_data *fixup = state->fixup;
+    struct vkd3d_shader_instruction *ins;
+    struct vsir_src_operand *src_params;
+    struct vsir_dst_operand *dst_params;
+    const struct sm6_value *ptr, *src;
+    struct vsir_operand regs[2], reg;
+    struct vsir_operand coord;
+
+    ptr = fixup->ptr;
+    src = fixup->src;
+    vsir_operand_from_dxil_value(&reg, ptr, 0, dxil);
+
+    if (reg.type != VKD3DSPR_GROUPSHAREDMEM)
+    {
+        vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
+                "The destination register for an atomicrmw instruction is not groupshared memory.");
+        return;
+    }
+
     if (ptr->structure_stride)
     {
         if (reg.idx[1].rel_addr)
             regs[0] = reg.idx[1].rel_addr->reg;
         else
-            register_make_constant_uint(&regs[0], reg.idx[1].offset);
-        register_make_constant_uint(&regs[1], 0);
-        if (!sm6_parser_emit_reg_composite_construct(sm6, regs, 2, state, &coord))
+            vsir_operand_init_constant_u32(&regs[0], reg.idx[1].offset);
+        vsir_operand_init_constant_u32(&regs[1], 0);
+        if (!sm6_parser_emit_reg_composite_construct(dxil, regs, 2, state, &coord))
             return;
     }
 
-    if (!(ins = sm6_parser_add_function_instruction(sm6, state)))
+    if (!(ins = sm6_parser_add_function_instruction(dxil, state)))
         return;
 
-    vsir_instruction_init(ins, &sm6->p.location, op);
-    ins->flags = is_volatile ? VKD3DARF_VOLATILE : 0;
+    vsir_instruction_init(ins, &dxil->p.location, fixup->op);
+    ins->flags = fixup->is_volatile ? VKD3DARF_VOLATILE : 0;
 
-    if (!(src_params = instruction_src_params_alloc(ins, 2, sm6)))
+    if (!(src_params = instruction_src_params_alloc(ins, 2, dxil)))
     {
         vkd3d_shader_instruction_make_nop(ins);
         return;
@@ -4985,16 +5040,16 @@ static void sm6_parser_emit_atomicrmw(struct sm6_parser *sm6, struct function_em
         src_param_init_vector_from_reg(&src_params[0], &coord);
     else
         src_param_make_constant_uint(&src_params[0], 0);
-    src_param_init_from_value(&src_params[1], src, 0, sm6);
+    src_param_init_from_value(&src_params[1], src, 0, dxil);
 
-    sm6_parser_init_ssa_value(sm6, dst);
+    sm6_parser_init_ssa_value(dxil, dst);
 
-    if (!(dst_params = instruction_dst_params_alloc(ins, 2, sm6)))
+    if (!(dst_params = instruction_dst_params_alloc(ins, 2, dxil)))
     {
         vkd3d_shader_instruction_make_nop(ins);
         return;
     }
-    vsir_register_from_dxil_value(&dst_params[0].reg, dst, 0, sm6);
+    vsir_operand_from_dxil_value(&dst_params[0].reg, dst, 0, dxil);
     dst_param_init(&dst_params[0]);
 
     dst_params[1].reg = reg;
@@ -5016,18 +5071,14 @@ static enum vkd3d_shader_opcode map_binary_op(uint64_t code, const struct sm6_ty
 
     if (!is_int && !sm6_type_is_floating_point(type_a))
     {
-        WARN("Argument type %u is not bool, int16/32/64 or floating point.\n", type_a->class);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "An argument to a binary operation is not bool, int16/32/64 or floating point.");
         return VSIR_OP_INVALID;
     }
+
     if (type_a != type_b)
-    {
-        WARN("Type mismatch, type %u width %u vs type %u width %u.\n", type_a->class,
-                type_a->u.width, type_b->class, type_b->u.width);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH,
                 "Type mismatch in binary operation arguments.");
-    }
 
     *aux_opcode = VSIR_OP_NOP;
 
@@ -5093,11 +5144,8 @@ static enum vkd3d_shader_opcode map_binary_op(uint64_t code, const struct sm6_ty
     }
 
     if (!is_valid)
-    {
-        WARN("Invalid operation %u for type %u, width %u.\n", op, type_a->class, type_a->u.width);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_INVALID_OPERATION,
                 "Binary operation %u is invalid on type class %u, width %u.", op, type_a->class, type_a->u.width);
-    }
 
     return op;
 }
@@ -5149,7 +5197,7 @@ static void sm6_parser_emit_binop(struct sm6_parser *sm6, struct function_emissi
         src_param_init_from_value(&src_params[0], b, DXIL_TYPE_SIGNED, sm6);
 
         dst_param_init(&dst_params[0]);
-        register_init_with_id(&dst_params[0].reg, VKD3DSPR_SSA, src_params[0].reg.data_type, aux_id);
+        vsir_operand_init_with_id(&dst_params[0].reg, VKD3DSPR_SSA, src_params[0].reg.data_type, aux_id);
     }
 
     if (!(ins = sm6_parser_add_function_instruction(sm6, state)))
@@ -5192,15 +5240,10 @@ static void sm6_parser_emit_binop(struct sm6_parser *sm6, struct function_emissi
     }
     /* The above flags are very common and cause warning spam. */
     if (flags && silence_warning)
-    {
         TRACE("Ignoring flags %#"PRIx64".\n", flags);
-    }
     else if (flags)
-    {
-        WARN("Ignoring flags %#"PRIx64".\n", flags);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Ignoring flags %#"PRIx64" for a binary operation.", flags);
-    }
 
     if (!(src_params = instruction_src_params_alloc(ins, 2, sm6)))
     {
@@ -5217,7 +5260,7 @@ static void sm6_parser_emit_binop(struct sm6_parser *sm6, struct function_emissi
     else
     {
         src_param_init(&src_params[1]);
-        register_init_with_id(&src_params[1].reg, VKD3DSPR_SSA, src_params[0].reg.data_type, aux_id);
+        vsir_operand_init_with_id(&src_params[1].reg, VKD3DSPR_SSA, src_params[0].reg.data_type, aux_id);
     }
 
     dst->type = a->type;
@@ -5247,6 +5290,32 @@ static bool sm6_function_validate_block_index(const struct sm6_function *functio
     return true;
 }
 
+static bool dxil_copy_template_operand(struct sm6_parser *dxil,
+        struct vsir_operand *operand, const struct vsir_operand *template)
+{
+    *operand = *template;
+
+    for (unsigned int i = 0; i < operand->idx_count; ++i)
+    {
+        operand->idx[i].rel_addr = NULL;
+    }
+
+    for (unsigned int i = 0; i < operand->idx_count; ++i)
+    {
+        if (!template->idx[i].rel_addr)
+            continue;
+        if (!(operand->idx[i].rel_addr = vsir_program_get_src_operands(dxil->program, 1)))
+        {
+            vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
+                    "Out of memory allocating indirect index for operand copy.");
+            return false;
+        }
+        *operand->idx[i].rel_addr = *template->idx[i].rel_addr;
+    }
+
+    return true;
+}
+
 static void sm6_parser_emit_br(struct sm6_parser *dxil, struct function_emission_state *state)
 {
     const struct dxil_record *record = state->record;
@@ -5314,8 +5383,8 @@ static void sm6_parser_emit_br(struct sm6_parser *dxil, struct function_emission
 }
 
 static bool sm6_parser_emit_reg_composite_construct(struct sm6_parser *sm6,
-        const struct vkd3d_shader_register *operand_regs, unsigned int component_count,
-        struct function_emission_state *state, struct vkd3d_shader_register *reg)
+        const struct vsir_operand *operand_regs, unsigned int component_count,
+        struct function_emission_state *state, struct vsir_operand *reg)
 {
     struct vkd3d_shader_instruction *ins;
     struct vsir_src_operand *src_params;
@@ -5330,11 +5399,11 @@ static bool sm6_parser_emit_reg_composite_construct(struct sm6_parser *sm6,
     }
 
     for (i = 0; i < component_count; ++i)
-        all_constant &= register_is_constant(&operand_regs[i]);
+        all_constant &= vsir_operand_is_constant(&operand_regs[i]);
 
     if (all_constant)
     {
-        vsir_register_init(reg, VKD3DSPR_IMMCONST, operand_regs[0].data_type, 0);
+        vsir_operand_init(reg, VKD3DSPR_IMMCONST, operand_regs[0].data_type, 0);
         reg->dimension = VSIR_DIMENSION_VEC4;
         for (i = 0; i < component_count; ++i)
             reg->u.immconst_u32[i] = operand_regs[i].u.immconst_u32[0];
@@ -5343,7 +5412,7 @@ static bool sm6_parser_emit_reg_composite_construct(struct sm6_parser *sm6,
         return true;
     }
 
-    register_init_with_id(reg, VKD3DSPR_TEMP, operand_regs[0].data_type, state->temp_idx++);
+    vsir_operand_init_with_id(reg, VKD3DSPR_TEMP, operand_regs[0].data_type, state->temp_idx++);
     reg->dimension = VSIR_DIMENSION_VEC4;
 
     for (i = 0; i < component_count; ++i)
@@ -5374,35 +5443,35 @@ error:
 }
 
 static bool sm6_parser_emit_composite_construct(struct sm6_parser *sm6, const struct sm6_value **operands,
-        unsigned int component_count, struct function_emission_state *state, struct vkd3d_shader_register *reg)
+        unsigned int component_count, struct function_emission_state *state, struct vsir_operand *reg)
 {
-    struct vkd3d_shader_register operand_regs[VKD3D_VEC4_SIZE];
+    struct vsir_operand operand_regs[VKD3D_VEC4_SIZE];
     unsigned int i;
 
     for (i = 0; i < component_count; ++i)
     {
-        vsir_register_from_dxil_value(&operand_regs[i], operands[i], 0, sm6);
+        vsir_operand_from_dxil_value(&operand_regs[i], operands[i], 0, sm6);
     }
 
     return sm6_parser_emit_reg_composite_construct(sm6, operand_regs, component_count, state, reg);
 }
 
 static bool sm6_parser_emit_coordinate_construct(struct sm6_parser *sm6, const struct sm6_value **operands,
-        unsigned int max_operands, const struct sm6_value *z_operand, struct function_emission_state *state,
-        struct vkd3d_shader_register *reg)
+        unsigned int max_operands, const struct sm6_value *z_operand,
+        struct function_emission_state *state, struct vsir_operand *reg)
 {
-    struct vkd3d_shader_register operand_regs[VKD3D_VEC4_SIZE];
+    struct vsir_operand operand_regs[VKD3D_VEC4_SIZE];
     unsigned int component_count;
 
     for (component_count = 0; component_count < max_operands; ++component_count)
     {
         if (!z_operand && operands[component_count]->value_type == VALUE_TYPE_UNDEFINED)
             break;
-        vsir_register_from_dxil_value(&operand_regs[component_count], operands[component_count], 0, sm6);
+        vsir_operand_from_dxil_value(&operand_regs[component_count], operands[component_count], 0, sm6);
     }
 
     if (z_operand)
-        vsir_register_from_dxil_value(&operand_regs[component_count++], z_operand, 0, sm6);
+        vsir_operand_from_dxil_value(&operand_regs[component_count++], z_operand, 0, sm6);
 
     return sm6_parser_emit_reg_composite_construct(sm6, operand_regs, component_count, state, reg);
 }
@@ -5635,9 +5704,9 @@ static void sm6_parser_emit_dx_atomic_binop(struct sm6_parser *sm6, enum dx_intr
     struct vsir_src_operand *src_params;
     struct vsir_dst_operand *dst_params;
     const struct sm6_value *resource;
-    struct vkd3d_shader_register reg;
     enum vkd3d_shader_opcode opcode;
     enum dxil_resource_kind kind;
+    struct vsir_operand reg;
 
     resource = operands[0];
     if (!sm6_value_validate_is_handle(resource, sm6))
@@ -5658,14 +5727,13 @@ static void sm6_parser_emit_dx_atomic_binop(struct sm6_parser *sm6, enum dx_intr
     }
     else
     {
-        vsir_register_from_dxil_value(&reg, operands[coord_idx], 0, sm6);
+        vsir_operand_from_dxil_value(&reg, operands[coord_idx], 0, sm6);
     }
 
     for (i = coord_idx + coord_count; i < coord_idx + 3; ++i)
     {
         if (operands[i]->value_type != VALUE_TYPE_UNDEFINED)
         {
-            WARN("Ignoring unexpected operand.\n");
             vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                     "Ignoring an unexpected defined operand value for atomic instruction %u.", opcode);
             break;
@@ -5697,7 +5765,7 @@ static void sm6_parser_emit_dx_atomic_binop(struct sm6_parser *sm6, enum dx_intr
     }
 
     dst_param_init(&dst_params[0]);
-    vsir_register_from_dxil_value(&dst_params[0].reg, dst, 0, sm6);
+    vsir_operand_from_dxil_value(&dst_params[0].reg, dst, 0, sm6);
     dst_param_init(&dst_params[1]);
     sm6_register_from_handle(sm6, &resource->u.handle, &dst_params[1].reg);
 }
@@ -5771,7 +5839,7 @@ static void sm6_parser_emit_dx_calculate_lod(struct sm6_parser *sm6, enum dx_int
     const struct sm6_value *resource, *sampler;
     struct vkd3d_shader_instruction *ins;
     struct vsir_src_operand *src_params;
-    struct vkd3d_shader_register coord;
+    struct vsir_operand coord;
     unsigned int clamp;
 
     resource = operands[0];
@@ -5841,7 +5909,7 @@ static void sm6_parser_emit_dx_cbuffer_load(struct sm6_parser *sm6, enum dx_intr
     if (data_type_is_64_bit(src_param->reg.data_type))
         src_param->swizzle = vsir_swizzle_64_from_32(src_param->swizzle);
     else
-        register_convert_to_minimum_precision(&src_param->reg);
+        vsir_operand_convert_to_minimum_precision(&src_param->reg);
 
     if (!instruction_dst_param_init_ssa_vector(ins, sm6_type_max_vector_size(type), sm6))
         vkd3d_shader_instruction_make_nop(ins);
@@ -5859,7 +5927,7 @@ static void sm6_parser_dcl_register_builtin(struct sm6_parser *dxil, enum vkd3d_
         if (!(ins = sm6_parser_add_instruction(dxil, opcode)))
             return;
         dst_param = &ins->declaration.dst;
-        vsir_register_init(&dst_param->reg, reg_type, data_type, 0);
+        vsir_operand_init(&dst_param->reg, reg_type, data_type, 0);
         dst_param_init_vector(dst_param, component_count);
     }
 }
@@ -5882,7 +5950,7 @@ static void sm6_parser_emit_dx_input_register_mov(struct sm6_parser *dxil, struc
     }
 
     sm6_parser_dcl_register_builtin(dxil, VSIR_OP_DCL_INPUT, reg_type, data_type, 1);
-    vsir_register_init(&src_param->reg, reg_type, data_type, 0);
+    vsir_operand_init(&src_param->reg, reg_type, data_type, 0);
     if (!scalar)
         src_param->reg.dimension = VSIR_DIMENSION_VEC4;
     src_param_init(src_param);
@@ -5969,7 +6037,7 @@ static void sm6_parser_emit_dx_stream(struct sm6_parser *dxil, enum dx_intrinsic
         vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "Output stream index %u is invalid.", i);
 
-    register_init_with_id(&src_param->reg, VKD3DSPR_STREAM, VSIR_DATA_UNUSED, i);
+    vsir_operand_init_with_id(&src_param->reg, VKD3DSPR_STREAM, VSIR_DATA_UNUSED, i);
     src_param_init(src_param);
 
     if (op == DX_EMIT_THEN_CUT_STREAM)
@@ -6022,7 +6090,7 @@ static void sm6_parser_emit_dx_domain_location(struct sm6_parser *dxil, enum dx_
     }
 
     sm6_parser_dcl_register_builtin(dxil, VSIR_OP_DCL_INPUT, VKD3DSPR_TESSCOORD, VSIR_DATA_F32, 3);
-    vsir_register_init(&src_param->reg, VKD3DSPR_TESSCOORD, VSIR_DATA_F32, 0);
+    vsir_operand_init(&src_param->reg, VKD3DSPR_TESSCOORD, VSIR_DATA_F32, 0);
     src_param->reg.dimension = VSIR_DIMENSION_VEC4;
     src_param_init_scalar(src_param, component_idx);
 
@@ -6034,10 +6102,10 @@ static void sm6_parser_emit_dx_dot(struct sm6_parser *dxil, enum dx_intrinsic_op
         const struct sm6_value **operands, struct function_emission_state *state)
 {
     struct vkd3d_shader_instruction *ins;
-    struct vkd3d_shader_register regs[2];
     struct vsir_src_operand *src_params;
     enum vkd3d_shader_opcode opcode;
     unsigned int component_count;
+    struct vsir_operand regs[2];
 
     switch (op)
     {
@@ -6120,7 +6188,11 @@ static void sm6_parser_emit_dx_eval_attrib(struct sm6_parser *sm6, enum dx_intri
         return;
     }
 
-    src_params[0].reg = sm6->input_params[row_index].reg;
+    if (!(dxil_copy_template_operand(sm6, &src_params[0].reg, &sm6->input_params[row_index].reg)))
+    {
+        vkd3d_shader_instruction_make_nop(ins);
+        return;
+    }
     src_param_init_scalar(&src_params[0], column_index);
     if (e->register_count > 1)
         register_index_address_init(&src_params[0].reg.idx[0], operands[1], sm6);
@@ -6195,7 +6267,7 @@ static void sm6_parser_emit_dx_compute_builtin(struct sm6_parser *dxil, enum dx_
         return;
     }
 
-    vsir_register_init(&src_param->reg, reg_type, VSIR_DATA_U32, 0);
+    vsir_operand_init(&src_param->reg, reg_type, VSIR_DATA_U32, 0);
     src_param->reg.dimension = VSIR_DIMENSION_VEC4;
     if (component_count > 1)
         component_idx = sm6_value_get_constant_uint(operands[0], dxil);
@@ -6442,7 +6514,11 @@ static void sm6_parser_emit_dx_load_input(struct sm6_parser *sm6, enum dx_intrin
         return;
     }
 
-    src_param->reg = params[row_index].reg;
+    if (!(dxil_copy_template_operand(sm6, &src_param->reg, &params[row_index].reg)))
+    {
+        vkd3d_shader_instruction_make_nop(ins);
+        return;
+    }
     src_param_init_scalar(src_param, column_index);
     count = 0;
 
@@ -6464,7 +6540,7 @@ static void sm6_parser_emit_dx_make_double(struct sm6_parser *dxil, enum dx_intr
 {
     struct vkd3d_shader_instruction *ins;
     struct vsir_src_operand *src_params;
-    struct vkd3d_shader_register reg;
+    struct vsir_operand reg;
 
     if (!sm6_parser_emit_composite_construct(dxil, &operands[0], 2, state, &reg))
         return;
@@ -6604,8 +6680,8 @@ static void sm6_parser_emit_dx_raw_buffer_store(struct sm6_parser *sm6, enum dx_
     struct vkd3d_shader_instruction *ins;
     struct vsir_src_operand *src_params;
     struct vsir_dst_operand *dst_param;
-    struct vkd3d_shader_register data;
     const struct sm6_value *resource;
+    struct vsir_operand data;
     bool raw;
 
     resource = operands[0];
@@ -6719,9 +6795,9 @@ static void sm6_parser_emit_dx_buffer_store(struct sm6_parser *sm6, enum dx_intr
     unsigned int write_mask, component_count;
     struct vkd3d_shader_instruction *ins;
     struct vsir_src_operand *src_params;
-    struct vkd3d_shader_register texel;
     struct vsir_dst_operand *dst_param;
     const struct sm6_value *resource;
+    struct vsir_operand texel;
 
     resource = operands[0];
     if (!sm6_value_validate_is_handle(resource, sm6))
@@ -6801,7 +6877,7 @@ static void sm6_parser_emit_dx_get_sample_count(struct sm6_parser *dxil, enum dx
         return;
     }
 
-    vsir_register_init(&src_param->reg, VKD3DSPR_RASTERIZER, VSIR_DATA_F32, 0);
+    vsir_operand_init(&src_param->reg, VKD3DSPR_RASTERIZER, VSIR_DATA_F32, 0);
     src_param->reg.dimension = VSIR_DIMENSION_VEC4;
     src_param_init(src_param);
 
@@ -6847,7 +6923,7 @@ static void sm6_parser_emit_dx_get_sample_pos(struct sm6_parser *dxil, enum dx_i
     else
     {
         src_param_init_vector(&src_params[0], 2);
-        vsir_register_init(&src_params[0].reg, VKD3DSPR_RASTERIZER, VSIR_DATA_F32, 0);
+        vsir_operand_init(&src_params[0].reg, VKD3DSPR_RASTERIZER, VSIR_DATA_F32, 0);
         src_params[0].reg.dimension = VSIR_DIMENSION_VEC4;
         src_param_init_from_value(&src_params[1], operands[0], 0, dxil);
     }
@@ -6876,10 +6952,10 @@ static void sm6_parser_emit_dx_sample(struct sm6_parser *dxil, enum dx_intrinsic
         const struct sm6_value **operands, struct function_emission_state *state)
 {
     unsigned int clamp_idx = 0, component_count = VKD3D_VEC4_SIZE;
-    struct vkd3d_shader_register coord, ddx, ddy;
     const struct sm6_value *resource, *sampler;
     struct vkd3d_shader_instruction *ins;
     struct vsir_src_operand *src_params;
+    struct vsir_operand coord, ddx, ddy;
 
     resource = operands[0];
     sampler = operands[1];
@@ -7009,7 +7085,11 @@ static void sm6_parser_emit_dx_sample_index(struct sm6_parser *dxil, enum dx_int
         return;
     }
 
-    src_param->reg = dxil->input_params[element_idx].reg;
+    if (!(dxil_copy_template_operand(dxil, &src_param->reg, &dxil->input_params[element_idx].reg)))
+    {
+        vkd3d_shader_instruction_make_nop(ins);
+        return;
+    }
     src_param_init(src_param);
 
     if (!instruction_dst_param_init_ssa_scalar(ins, 0, dxil))
@@ -7070,6 +7150,7 @@ static void sm6_parser_emit_dx_store_output(struct sm6_parser *dxil, enum dx_int
     const struct shader_signature *signature;
     struct vkd3d_shader_instruction *ins;
     unsigned int row_index, column_index;
+    const struct vsir_operand *template;
     struct vsir_src_operand *src_param;
     struct vsir_dst_operand *dst_param;
     const struct signature_element *e;
@@ -7115,8 +7196,12 @@ static void sm6_parser_emit_dx_store_output(struct sm6_parser *dxil, enum dx_int
     }
 
     dst_param_init_scalar(dst_param, column_index);
-    dst_param->reg = is_patch_constant ? dxil->patch_constant_params[row_index].reg
-            : dxil->output_params[row_index].reg;
+    template = is_patch_constant ? &dxil->patch_constant_params[row_index].reg : &dxil->output_params[row_index].reg;
+    if (!(dxil_copy_template_operand(dxil, &dst_param->reg, template)))
+    {
+        vkd3d_shader_instruction_make_nop(ins);
+        return;
+    }
     if (e->register_count > 1)
         register_index_address_init(&dst_param->reg.idx[0], operands[1], dxil);
 
@@ -7136,10 +7221,10 @@ static void sm6_parser_emit_dx_store_output(struct sm6_parser *dxil, enum dx_int
 static void sm6_parser_emit_dx_texture_gather(struct sm6_parser *dxil, enum dx_intrinsic_opcode op,
         const struct sm6_value **operands, struct function_emission_state *state)
 {
-    struct vkd3d_shader_register coord, offset;
     const struct sm6_value *resource, *sampler;
     struct vkd3d_shader_instruction *ins;
     struct vsir_src_operand *src_params;
+    struct vsir_operand coord, offset;
     unsigned int swizzle;
     bool extended_offset;
 
@@ -7210,9 +7295,9 @@ static void sm6_parser_emit_dx_texture_load(struct sm6_parser *dxil, enum dx_int
     const struct sm6_value *resource, *mip_level_or_sample_count;
     struct vkd3d_shader_instruction *ins;
     struct vsir_src_operand *src_params;
-    struct vkd3d_shader_register coord;
     enum dxil_resource_kind kind;
     bool is_multisample, is_uav;
+    struct vsir_operand coord;
     unsigned int i;
 
     resource = operands[0];
@@ -7256,12 +7341,12 @@ static void sm6_parser_emit_dx_texture_load(struct sm6_parser *dxil, enum dx_int
 static void sm6_parser_emit_dx_texture_store(struct sm6_parser *dxil, enum dx_intrinsic_opcode op,
         const struct sm6_value **operands, struct function_emission_state *state)
 {
-    struct vkd3d_shader_register coord, texel;
     unsigned int write_mask, component_count;
     struct vkd3d_shader_instruction *ins;
     struct vsir_src_operand *src_params;
     struct vsir_dst_operand *dst_param;
     const struct sm6_value *resource;
+    struct vsir_operand coord, texel;
 
     resource = operands[0];
     if (!sm6_value_validate_is_texture_handle(resource, op, dxil))
@@ -7347,7 +7432,6 @@ static enum vkd3d_shader_opcode sm6_dx_map_wave_bit_op(enum dxil_wave_bit_op_kin
         case WAVE_BIT_OP_XOR:
             return VSIR_OP_WAVE_ACTIVE_BIT_XOR;
         default:
-            FIXME("Unhandled wave bit op %u.\n", op);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_UNHANDLED_INTRINSIC,
                     "Wave bit operation %u is unhandled.", op);
             return VSIR_OP_INVALID;
@@ -7402,7 +7486,6 @@ static enum vkd3d_shader_opcode sm6_dx_map_wave_op(enum dxil_wave_op_kind op, bo
                 return VSIR_OP_WAVE_OP_MAX;
             return is_signed ? VSIR_OP_WAVE_OP_IMAX : VSIR_OP_WAVE_OP_UMAX;
         default:
-            FIXME("Unhandled wave op %u.\n", op);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_UNHANDLED_INTRINSIC,
                     "Wave operation %u is unhandled.", op);
             return VSIR_OP_INVALID;
@@ -7683,7 +7766,6 @@ static bool sm6_parser_validate_dx_op(struct sm6_parser *sm6, enum dx_intrinsic_
         const struct sm6_value *value = operands[i];
         if (!sm6_parser_validate_operand_type(sm6, value, info->operand_info[i], dst->type))
         {
-            WARN("Failed to validate operand %u for dx intrinsic id %u, '%s'.\n", i + 1, op, name);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                     "Operand %u for call to dx intrinsic function '%s' is invalid.", i + 1, name);
             return false;
@@ -7691,7 +7773,6 @@ static bool sm6_parser_validate_dx_op(struct sm6_parser *sm6, enum dx_intrinsic_
     }
     if (info->operand_info[operand_count])
     {
-        WARN("Missing operands for dx intrinsic id %u, '%s'.\n", op, name);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
                 "Call to dx intrinsic function '%s' has missing operands.", name);
         return false;
@@ -7750,7 +7831,6 @@ static void sm6_parser_emit_call(struct sm6_parser *sm6, struct function_emissio
         return;
     if (!sm6_value_is_function_dcl(fn_value))
     {
-        WARN("Function target value is not a function declaration.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "Function call target value is not a function declaration.");
         return;
@@ -7766,7 +7846,6 @@ static void sm6_parser_emit_call(struct sm6_parser *sm6, struct function_emissio
     operand_count = type->u.function->param_count;
     if (operand_count > ARRAY_SIZE(operands))
     {
-        WARN("Ignoring %zu operands.\n", operand_count - ARRAY_SIZE(operands));
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Ignoring %zu operands for function call.", operand_count - ARRAY_SIZE(operands));
         operand_count = ARRAY_SIZE(operands);
@@ -7778,15 +7857,11 @@ static void sm6_parser_emit_call(struct sm6_parser *sm6, struct function_emissio
             return;
     }
     if ((j = record->operand_count - i))
-    {
-        WARN("Ignoring %u operands beyond the function parameter list.\n", j);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Ignoring %u function call operands beyond the parameter list.", j);
-    }
 
     if (!fn_value->u.function.is_prototype)
     {
-        FIXME("Unhandled call to local function.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "Call to a local function is unsupported.");
         return;
@@ -7796,7 +7871,6 @@ static void sm6_parser_emit_call(struct sm6_parser *sm6, struct function_emissio
 
     if (!operand_count)
     {
-        WARN("Missing dx intrinsic function id.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
                 "The id for a dx intrinsic function is missing.");
         return;
@@ -7805,7 +7879,6 @@ static void sm6_parser_emit_call(struct sm6_parser *sm6, struct function_emissio
     op_value = operands[0];
     if (!sm6_value_is_constant(op_value) || !sm6_type_is_integer(op_value->type))
     {
-        WARN("dx intrinsic function id is not a constant int.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                 "Expected a constant integer dx intrinsic function id.");
         return;
@@ -8130,27 +8203,14 @@ static void sm6_parser_emit_cmpxchg(struct sm6_parser *dxil, struct function_emi
     const struct dxil_record *record = state->record;
     uint64_t success_ordering, failure_ordering;
     const struct sm6_value *ptr, *cmp, *new;
-    struct vkd3d_shader_instruction *ins;
-    struct vsir_src_operand *src_params;
-    struct vsir_dst_operand *dst_params;
-    struct vkd3d_shader_register reg;
+    struct fixup_data *fixup;
     unsigned int i = 0;
     bool is_volatile;
     uint64_t code;
 
-    if (!(ptr = sm6_parser_get_value_by_ref(dxil, record, NULL, &i))
-            || !sm6_value_validate_is_pointer_to_i32(ptr, dxil)
-            || !sm6_value_validate_is_backward_ref(ptr, dxil))
-        return;
-
-    vsir_register_from_dxil_value(&reg, ptr, 0, dxil);
-
-    if (reg.type != VKD3DSPR_GROUPSHAREDMEM)
-    {
-        vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
-                "The destination register for a cmpxchg instruction is not groupshared memory.");
+    if (!(ptr = sm6_parser_get_pointer_value_by_ref(dxil, record, NULL, &i))
+            || !sm6_value_validate_is_pointer_to_i32(ptr, dxil))
         return;
-    }
 
     if (!(dst->type = sm6_type_get_cmpxchg_result_struct(dxil)))
     {
@@ -8190,11 +8250,44 @@ static void sm6_parser_emit_cmpxchg(struct sm6_parser *dxil, struct function_emi
     if (record->operand_count > i && record->operands[i])
         FIXME("Ignoring weak cmpxchg.\n");
 
+    sm6_parser_init_ssa_value(dxil, dst);
+
+    if (!(fixup = sm6_parser_add_fixup(dxil, state, FIXUP_CMPXCHG)))
+        return;
+
+    fixup->ptr = ptr;
+    fixup->cmp = cmp;
+    fixup->new = new;
+    fixup->is_volatile = is_volatile;
+}
+
+static void sm6_parser_fixup_cmpxchg(struct sm6_parser *dxil, struct function_emission_state *state)
+{
+    struct sm6_value *dst = sm6_parser_get_current_value(dxil);
+    const struct sm6_value *ptr, *cmp, *new;
+    struct fixup_data *fixup = state->fixup;
+    struct vkd3d_shader_instruction *ins;
+    struct vsir_src_operand *src_params;
+    struct vsir_dst_operand *dst_params;
+    struct vsir_operand reg;
+
+    ptr = fixup->ptr;
+    cmp = fixup->cmp;
+    new = fixup->new;
+    vsir_operand_from_dxil_value(&reg, ptr, 0, dxil);
+
+    if (reg.type != VKD3DSPR_GROUPSHAREDMEM)
+    {
+        vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
+                "The destination register for a cmpxchg instruction is not groupshared memory.");
+        return;
+    }
+
     if (!(ins = sm6_parser_add_function_instruction(dxil, state)))
         return;
 
     vsir_instruction_init(ins, &dxil->p.location, VSIR_OP_IMM_ATOMIC_CMP_EXCH);
-    ins->flags = is_volatile ? VKD3DARF_VOLATILE : 0;
+    ins->flags = fixup->is_volatile ? VKD3DARF_VOLATILE : 0;
 
     if (!(src_params = instruction_src_params_alloc(ins, 3, dxil)))
     {
@@ -8214,7 +8307,7 @@ static void sm6_parser_emit_cmpxchg(struct sm6_parser *dxil, struct function_emi
         return;
     }
 
-    vsir_register_from_dxil_value(&dst_params[0].reg, dst, 0, dxil);
+    vsir_operand_from_dxil_value(&dst_params[0].reg, dst, 0, dxil);
     dst_param_init(&dst_params[0]);
     dst_params[1].reg = reg;
     dst_param_init(&dst_params[1]);
@@ -8278,7 +8371,7 @@ static void sm6_parser_emit_extractval(struct sm6_parser *dxil, struct function_
         return;
     }
 
-    vsir_register_from_dxil_value(&src_param->reg, src, 0, dxil);
+    vsir_operand_from_dxil_value(&src_param->reg, src, 0, dxil);
     src_param_init_scalar(src_param, elem_idx);
 
     if (!instruction_dst_param_init_ssa_scalar(ins, 0, dxil))
@@ -8376,18 +8469,15 @@ static void sm6_parser_emit_load(struct sm6_parser *dxil, struct function_emissi
     struct sm6_value *dst = sm6_parser_get_current_value(dxil);
     const struct sm6_type *elem_type = NULL, *pointee_type;
     const struct dxil_record *record = state->record;
-    unsigned int alignment, operand_count, i = 0;
-    struct vkd3d_shader_instruction *ins;
-    struct vsir_src_operand *src_params;
-    struct vkd3d_shader_register reg;
+    unsigned int alignment, i = 0;
     const struct sm6_value *ptr;
+    struct fixup_data *fixup;
     uint64_t alignment_code;
 
-    if (!(ptr = sm6_parser_get_value_by_ref(dxil, record, NULL, &i)))
+    if (!(ptr = sm6_parser_get_pointer_value_by_ref(dxil, record, NULL, &i)))
         return;
     if (!sm6_value_validate_is_register(ptr, dxil)
             || !sm6_value_validate_is_pointer(ptr, dxil)
-            || !sm6_value_validate_is_backward_ref(ptr, dxil)
             || !dxil_record_validate_operand_count(record, i + 2, i + 3, dxil))
         return;
 
@@ -8412,7 +8502,26 @@ static void sm6_parser_emit_load(struct sm6_parser *dxil, struct function_emissi
     if (record->operands[i])
         WARN("Ignoring volatile modifier.\n");
 
-    vsir_register_from_dxil_value(&reg, ptr, 0, dxil);
+    sm6_parser_init_ssa_value(dxil, dst);
+
+    if (!(fixup = sm6_parser_add_fixup(dxil, state, FIXUP_LOAD)))
+        return;
+
+    fixup->ptr = ptr;
+    fixup->alignment = alignment;
+}
+
+static void sm6_parser_fixup_load(struct sm6_parser *dxil, struct function_emission_state *state)
+{
+    struct fixup_data *fixup = state->fixup;
+    struct vkd3d_shader_instruction *ins;
+    struct vsir_src_operand *src_params;
+    const struct sm6_value *ptr;
+    unsigned int operand_count;
+    struct vsir_operand reg;
+
+    ptr = fixup->ptr;
+    vsir_operand_from_dxil_value(&reg, ptr, 0, dxil);
 
     if (!(ins = sm6_parser_add_function_instruction(dxil, state)))
         return;
@@ -8435,7 +8544,7 @@ static void sm6_parser_emit_load(struct sm6_parser *dxil, struct function_emissi
         /* Struct offset is always zero as there is no struct, just an array. */
         src_param_make_constant_uint(&src_params[1], 0);
         src_param_init_from_value(&src_params[2], ptr, 0, dxil);
-        src_params[2].reg.alignment = alignment;
+        src_params[2].reg.alignment = fixup->alignment;
         /* The offset is already in src_params[0]. */
         src_params[2].reg.idx_count = 1;
     }
@@ -8453,7 +8562,7 @@ static void sm6_parser_emit_load(struct sm6_parser *dxil, struct function_emissi
         if (operand_count > 1)
             src_param_make_constant_uint(&src_params[0], 0);
         src_param_init_from_value(&src_params[operand_count - 1], ptr, 0, dxil);
-        src_params[operand_count - 1].reg.alignment = alignment;
+        src_params[operand_count - 1].reg.alignment = fixup->alignment;
     }
 
     if (!instruction_dst_param_init_ssa_scalar(ins, 0, dxil))
@@ -8597,18 +8706,14 @@ static void sm6_parser_emit_ret(struct sm6_parser *dxil, struct function_emissio
 static void sm6_parser_emit_store(struct sm6_parser *dxil, struct function_emission_state *state)
 {
     const struct dxil_record *record = state->record;
-    unsigned int i = 0, alignment, operand_count;
-    struct vkd3d_shader_instruction *ins;
-    struct vsir_src_operand *src_params;
-    struct vsir_dst_operand *dst_param;
     const struct sm6_value *ptr, *src;
-    struct vkd3d_shader_register reg;
+    unsigned int i = 0, alignment;
+    struct fixup_data *fixup;
     uint64_t alignment_code;
 
-    if (!(ptr = sm6_parser_get_value_by_ref(dxil, record, NULL, &i))
+    if (!(ptr = sm6_parser_get_pointer_value_by_ref(dxil, record, NULL, &i))
             || !sm6_value_validate_is_register(ptr, dxil)
-            || !sm6_value_validate_is_pointer(ptr, dxil)
-            || !sm6_value_validate_is_backward_ref(ptr, dxil))
+            || !sm6_value_validate_is_pointer(ptr, dxil))
         return;
 
     /* Forward-referenced sources are stored as value/type pairs, even
@@ -8632,7 +8737,27 @@ static void sm6_parser_emit_store(struct sm6_parser *dxil, struct function_emiss
     if (record->operands[i])
         WARN("Ignoring volatile modifier.\n");
 
-    vsir_register_from_dxil_value(&reg, ptr, 0, dxil);
+    if (!(fixup = sm6_parser_add_fixup(dxil, state, FIXUP_STORE)))
+        return;
+
+    fixup->ptr = ptr;
+    fixup->src = src;
+    fixup->alignment = alignment;
+}
+
+static void sm6_parser_fixup_store(struct sm6_parser *dxil, struct function_emission_state *state)
+{
+    struct fixup_data *fixup = state->fixup;
+    struct vkd3d_shader_instruction *ins;
+    struct vsir_src_operand *src_params;
+    struct vsir_dst_operand *dst_param;
+    const struct sm6_value *ptr, *src;
+    unsigned int operand_count;
+    struct vsir_operand reg;
+
+    ptr = fixup->ptr;
+    src = fixup->src;
+    vsir_operand_from_dxil_value(&reg, ptr, 0, dxil);
 
     if (!(ins = sm6_parser_add_function_instruction(dxil, state)))
         return;
@@ -8680,7 +8805,7 @@ static void sm6_parser_emit_store(struct sm6_parser *dxil, struct function_emiss
 
     dst_param_init(dst_param);
     dst_param->reg = reg;
-    dst_param->reg.alignment = alignment;
+    dst_param->reg.alignment = fixup->alignment;
     /* Groupshared stores contain the address in the src params. */
     if (dst_param->reg.type != VKD3DSPR_IDXTEMP)
         dst_param->reg.idx_count = 1;
@@ -8924,14 +9049,12 @@ static void sm6_parser_metadata_attachment_block_init(struct sm6_parser *sm6, co
         record = block->records[i];
         if (record->code != METADATA_ATTACHMENT)
         {
-            WARN("Ignoring record with code %u.\n", record->code);
             vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_ATTACHMENT,
                     "Ignoring a metadata attachment record with code %u.", record->code);
             continue;
         }
         if (!(record->operand_count & 1))
         {
-            WARN("Ignoring function attachment.\n");
             vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_ATTACHMENT,
                     "Ignoring a metadata function attachment.");
             continue;
@@ -8940,7 +9063,6 @@ static void sm6_parser_metadata_attachment_block_init(struct sm6_parser *sm6, co
         index = record->operands[0];
         if (!target_block->record_count || index >= target_block->record_count - 1)
         {
-            WARN("Invalid record index %"PRIu64".\n", index);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
                     "Invalid record index %"PRIu64" for a metadata attachment.", index);
             continue;
@@ -8948,11 +9070,8 @@ static void sm6_parser_metadata_attachment_block_init(struct sm6_parser *sm6, co
         /* 'index' is an instruction index, but records[0] is DECLAREBLOCKS, not an instruction. */
         target_record = target_block->records[index + 1];
         if (target_record->attachment)
-        {
-            WARN("Overwriting record attachment.\n");
             vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_ATTACHMENT,
                     "The target record for a metadata attachment already has an attachment.");
-        }
         target_record->attachment = record;
     }
 }
@@ -9004,7 +9123,6 @@ static bool metadata_node_get_unary_uint(const struct sm6_metadata_node *node, u
 {
     if (node->operand_count != 1)
     {
-        FIXME("Ignoring node with %u operands.\n", node->operand_count);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_ATTACHMENT,
                 "Ignoring metadata attachment node with %u operands; expected unary.", node->operand_count);
         return false;
@@ -9012,7 +9130,6 @@ static bool metadata_node_get_unary_uint(const struct sm6_metadata_node *node, u
     if (!sm6_metadata_value_is_value(node->operands[0])
             || !sm6_metadata_get_uint_value(sm6, node->operands[0], operand))
     {
-        WARN("Failed to get operand value.\n");
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_ATTACHMENT,
                 "Failed to get a metadata attachment operand value; ignoring the attachment.");
         return false;
@@ -9198,7 +9315,6 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6,
         dst = sm6_parser_get_current_value(sm6);
         fwd_type = dst->type;
         dst->type = NULL;
-        dst->is_back_ref = true;
         is_terminator = false;
 
         record = block->records[i];
@@ -9276,11 +9392,8 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6,
         }
 
         if (dst->type && fwd_type && dst->type != fwd_type)
-        {
-            WARN("Type mismatch.\n");
             vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH,
                     "The type of a result value does not match the type defined by a forward reference.");
-        }
 
         sm6->value_count += !!dst->type;
     }
@@ -9291,6 +9404,11 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6,
         return VKD3D_ERROR_INVALID_SHADER;
     }
 
+    sm6_parser_apply_fixups(sm6, function);
+
+    if (sm6->p.status < 0)
+        return sm6->p.status;
+
     return VKD3D_OK;
 }
 
@@ -9595,14 +9713,12 @@ static enum vkd3d_result metadata_value_create_node(struct sm6_metadata_value *m
     m->type = VKD3D_METADATA_NODE;
     if (!(m->value_type = sm6->metadata_type))
     {
-        WARN("Metadata type not found.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA,
                 "The type for metadata values was not found.");
         return VKD3D_ERROR_INVALID_SHADER;
     }
     if (!(node = vkd3d_malloc(offsetof(struct sm6_metadata_node, operands[record->operand_count]))))
     {
-        ERR("Failed to allocate metadata node with %u operands.\n", record->operand_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
                 "Out of memory allocating a metadata node with %u operands.", record->operand_count);
         return VKD3D_ERROR_OUT_OF_MEMORY;
@@ -9620,7 +9736,6 @@ static enum vkd3d_result metadata_value_create_node(struct sm6_metadata_value *m
         ref = record->operands[i] - offset;
         if (record->operands[i] >= offset && ref >= end_count)
         {
-            WARN("Invalid metadata index %"PRIu64".\n", ref);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA,
                     "Metadata index %"PRIu64" is invalid.", ref);
             vkd3d_free(node);
@@ -9629,7 +9744,6 @@ static enum vkd3d_result metadata_value_create_node(struct sm6_metadata_value *m
 
         if (!node->is_distinct && ref == dst_idx)
         {
-            WARN("Metadata self-reference at index %u.\n", dst_idx);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA,
                     "Metadata index %u is self-referencing.", dst_idx);
             vkd3d_free(node);
@@ -9639,7 +9753,6 @@ static enum vkd3d_result metadata_value_create_node(struct sm6_metadata_value *m
         node->operands[i] = (record->operands[i] >= offset) ? &table->values[ref] : NULL;
         if (record->code == METADATA_NAMED_NODE && !sm6_metadata_value_is_node(node->operands[i]))
         {
-            WARN("Named node operand is not a node.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA,
                     "The operand of a metadata named node is not a node.");
             vkd3d_free(node);
@@ -9667,7 +9780,6 @@ static enum vkd3d_result sm6_parser_metadata_init(struct sm6_parser *sm6, const
 
     if (!(values = vkd3d_calloc(count, sizeof(*values))))
     {
-        ERR("Failed to allocate metadata tables.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
                 "Out of memory allocating metadata tables.");
         return VKD3D_ERROR_OUT_OF_MEMORY;
@@ -9686,7 +9798,6 @@ static enum vkd3d_result sm6_parser_metadata_init(struct sm6_parser *sm6, const
             case METADATA_NAMED_NODE:
                 if (!name)
                 {
-                    WARN("Named node has no name.\n");
                     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA,
                             "A metadata named node has no name.");
                     return VKD3D_ERROR_INVALID_SHADER;
@@ -9727,7 +9838,6 @@ static enum vkd3d_result sm6_parser_metadata_init(struct sm6_parser *sm6, const
                 /* Check the next record to avoid freeing 'name' in all exit paths. */
                 if (i + 1 == block->record_count || block->records[i + 1]->code != METADATA_NAMED_NODE)
                 {
-                    WARN("Name is not followed by a named node.\n");
                     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA,
                             "A metadata node name is not followed by a named node.");
                     return VKD3D_ERROR_INVALID_SHADER;
@@ -9767,7 +9877,6 @@ static enum vkd3d_result sm6_parser_metadata_init(struct sm6_parser *sm6, const
                 if (!sm6_value_is_constant(value) && !sm6_value_is_undef(value)
                         && !sm6_value_is_constant_array(value) && !sm6_value_is_function_dcl(value))
                 {
-                    WARN("Value at index %u is not a constant or a function declaration.\n", value_idx);
                     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA,
                             "Metadata value at index %u is not a constant or a function declaration.", value_idx);
                     return VKD3D_ERROR_INVALID_SHADER;
@@ -9775,16 +9884,12 @@ static enum vkd3d_result sm6_parser_metadata_init(struct sm6_parser *sm6, const
                 m->u.value = value;
 
                 if (value->type != m->value_type)
-                {
-                    WARN("Type mismatch.\n");
                     vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH,
                             "The type of a metadata value does not match its referenced value at index %u.", value_idx);
-                }
 
                 break;
 
             default:
-                FIXME("Unhandled metadata type %u.\n", record->code);
                 vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA,
                         "Metadata type %u is unhandled.", record->code);
                 return VKD3D_ERROR_INVALID_SHADER;
@@ -9869,6 +9974,7 @@ static const enum vkd3d_shader_sysval_semantic sysval_semantic_table[] =
     [SEMANTIC_KIND_DEPTH]                = VKD3D_SHADER_SV_DEPTH,
     [SEMANTIC_KIND_DEPTHLESSEQUAL]       = VKD3D_SHADER_SV_DEPTH_LESS_EQUAL,
     [SEMANTIC_KIND_DEPTHGREATEREQUAL]    = VKD3D_SHADER_SV_DEPTH_GREATER_EQUAL,
+    [SEMANTIC_KIND_STENCILREF]           = VKD3D_SHADER_SV_STENCIL_REF,
 };
 
 static enum vkd3d_shader_sysval_semantic sysval_semantic_from_dxil_semantic_kind(enum dxil_semantic_kind kind,
@@ -9928,11 +10034,8 @@ static const struct sm6_metadata_value *sm6_parser_find_named_metadata(struct sm
         if (!node->operand_count)
             return NULL;
         if (node->operand_count > 1)
-        {
-            FIXME("Ignoring %u extra operands for %s.\n", node->operand_count - 1, name);
             vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                     "Ignoring %u extra operands for metadata node %s.", node->operand_count - 1, name);
-        }
         return node->operands[0];
     }
 
@@ -9940,7 +10043,7 @@ static const struct sm6_metadata_value *sm6_parser_find_named_metadata(struct sm
 }
 
 static bool sm6_parser_resources_load_register_range(struct sm6_parser *sm6,
-        const struct sm6_metadata_node *node, struct vkd3d_shader_register_range *range)
+        const struct sm6_metadata_node *node, struct vsir_register_range *range)
 {
     unsigned int size;
 
@@ -9950,11 +10053,8 @@ static bool sm6_parser_resources_load_register_range(struct sm6_parser *sm6,
         return false;
     }
     if (!sm6_type_is_pointer(node->operands[1]->value_type))
-    {
-        WARN("Resource type is not a pointer.\n");
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH,
                 "Resource metadata value type is not a pointer.");
-    }
 
     if (!sm6_metadata_get_uint_value(sm6, node->operands[3], &range->space))
     {
@@ -10034,11 +10134,8 @@ static bool resources_load_additional_values(struct resource_additional_values *
     info->byte_stride = 0;
 
     if (node->operand_count & 1)
-    {
-        WARN("Operand count is not even.\n");
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Operand count for resource descriptor tag/value pairs is not even.");
-    }
     operand_count = node->operand_count & ~1u;
 
     for (i = 0; i < operand_count; i += 2)
@@ -10046,7 +10143,6 @@ static bool resources_load_additional_values(struct resource_additional_values *
         if (!sm6_metadata_get_uint_value(sm6, node->operands[i], &tag)
                 || !sm6_metadata_get_uint_value(sm6, node->operands[i + 1], &value))
         {
-            WARN("Failed to load tag/value pair at index %u.\n", i);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                     "Resource descriptor tag/value pair at index %u is not an integer pair.", i);
             return false;
@@ -10057,7 +10153,6 @@ static bool resources_load_additional_values(struct resource_additional_values *
             case RESOURCE_TAG_ELEMENT_TYPE:
                 if (value && kind != RESOURCE_KIND_TYPEDBUFFER && !resource_kind_is_texture(kind))
                 {
-                    WARN("Invalid type %u for an untyped resource.\n", value);
                     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                             "An untyped resource has type %u.", value);
                     return false;
@@ -10068,7 +10163,6 @@ static bool resources_load_additional_values(struct resource_additional_values *
             case RESOURCE_TAG_ELEMENT_STRIDE:
                 if (value && kind != RESOURCE_KIND_STRUCTUREDBUFFER)
                 {
-                    WARN("Invalid stride %u for an unstructured resource.\n", value);
                     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                             "An unstructured resource has a byte stride.");
                     return false;
@@ -10078,22 +10172,17 @@ static bool resources_load_additional_values(struct resource_additional_values *
 
             case RESOURCE_TAG_SAMPLER_FEEDBACK_KIND:
                 /* MinMip = 0, MipRegionUsed = 1 */
-                FIXME("Unhandled sampler feedback kind %u.\n", value);
                 vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                         "Sampler feedback kind %u is unhandled.", value);
                 break;
 
             case RESOURCE_TAG_ENABLE_ATOMIC_64:
                 if (value)
-                {
-                    FIXME("Unsupported 64-bit atomic ops.\n");
                     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                             "64-bit atomic ops on resources are not supported.");
-                }
                 break;
 
             default:
-                FIXME("Unhandled tag %u, value %u.\n", tag, value);
                 vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                         "Tag %u for resource descriptor additional value %u is unhandled.", tag, value);
                 break;
@@ -10113,7 +10202,6 @@ static struct vkd3d_shader_resource *sm6_parser_resources_load_common_info(struc
 
     if (!(resource_type = shader_resource_type_from_dxil_resource_kind(kind)))
     {
-        FIXME("Unhandled resource kind %u.\n", kind);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                 "Resource kind %u is unhandled.", kind);
         return NULL;
@@ -10129,7 +10217,6 @@ static struct vkd3d_shader_resource *sm6_parser_resources_load_common_info(struc
 
     if (!sm6_metadata_value_is_node(m))
     {
-        WARN("Resource metadata list is not a node.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                 "Resource descriptor metadata list is not a node.");
         return NULL;
@@ -10141,10 +10228,8 @@ static struct vkd3d_shader_resource *sm6_parser_resources_load_common_info(struc
     if (kind == RESOURCE_KIND_TYPEDBUFFER || resource_kind_is_texture(kind))
     {
         if (resource_values.data_type == VSIR_DATA_UNUSED)
-        {
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                     "A typed resource has no data type.");
-        }
 
         ins->opcode = is_uav ? VSIR_OP_DCL_UAV_TYPED : VSIR_OP_DCL;
         for (i = 0; i < VKD3D_VEC4_SIZE; ++i)
@@ -10169,18 +10254,14 @@ static struct vkd3d_shader_resource *sm6_parser_resources_load_common_info(struc
 
         /* TODO: 16-bit resources. */
         if (ins->declaration.structured_resource.byte_stride % 4u)
-        {
-            WARN("Byte stride %u is not a multiple of 4.\n", ins->declaration.structured_resource.byte_stride);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                     "Structured resource byte stride %u is not a multiple of 4.",
                     ins->declaration.structured_resource.byte_stride);
-        }
 
         return &ins->declaration.structured_resource.resource;
     }
     else
     {
-        FIXME("Unhandled resource kind %u.\n", kind);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                 "Resource kind %u is unhandled.", kind);
     }
@@ -10190,13 +10271,13 @@ static struct vkd3d_shader_resource *sm6_parser_resources_load_common_info(struc
 
 static void init_resource_declaration(struct vkd3d_shader_resource *resource,
         enum vkd3d_shader_register_type reg_type, enum vsir_data_type data_type,
-        unsigned int id, const struct vkd3d_shader_register_range *range)
+        unsigned int id, const struct vsir_register_range *range)
 {
     struct vsir_dst_operand *param = &resource->reg;
 
     param->modifiers = 0;
     param->shift = 0;
-    vsir_register_init(&param->reg, reg_type, data_type, 3);
+    vsir_operand_init(&param->reg, reg_type, data_type, 3);
     param->reg.idx[0].offset = id;
     param->reg.idx[1].offset = range->first;
     param->reg.idx[2].offset = range->last;
@@ -10212,21 +10293,16 @@ static enum vkd3d_result sm6_parser_resources_load_srv(struct sm6_parser *sm6,
 
     if (node->operand_count < 9)
     {
-        WARN("Invalid operand count %u.\n", node->operand_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
                 "Invalid operand count %u for an SRV descriptor.", node->operand_count);
         return VKD3D_ERROR_INVALID_SHADER;
     }
     if (node->operand_count > 9)
-    {
-        WARN("Ignoring %u extra operands.\n", node->operand_count - 9);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Ignoring %u extra operands for an SRV descriptor.", node->operand_count - 9);
-    }
 
     if (!sm6_metadata_get_uint_value(sm6, node->operands[6], &kind))
     {
-        WARN("Failed to load resource type.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                 "SRV resource type metadata value is not an integer.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -10250,7 +10326,6 @@ static enum vkd3d_result sm6_parser_resources_load_srv(struct sm6_parser *sm6,
     {
         if (!sm6_metadata_get_uint_value(sm6, node->operands[7], &ins->declaration.semantic.sample_count))
         {
-            WARN("Failed to load sample count.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                     "SRV sample count metadata value is not an integer.");
             return VKD3D_ERROR_INVALID_SHADER;
@@ -10258,7 +10333,6 @@ static enum vkd3d_result sm6_parser_resources_load_srv(struct sm6_parser *sm6,
     }
     else if (!sm6_metadata_value_is_zero_or_undef(node->operands[7]))
     {
-        WARN("Ignoring sample count value.\n");
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Ignoring an SRV descriptor sample count metadata value which is not constant zero or undefined.");
     }
@@ -10274,23 +10348,18 @@ static enum vkd3d_result sm6_parser_resources_load_uav(struct sm6_parser *sm6,
 
     if (node->operand_count < 11)
     {
-        WARN("Invalid operand count %u.\n", node->operand_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
                 "Invalid operand count %u for a UAV descriptor.", node->operand_count);
         return VKD3D_ERROR_INVALID_SHADER;
     }
     if (node->operand_count > 11)
-    {
-        WARN("Ignoring %u extra operands.\n", node->operand_count - 11);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Ignoring %u extra operands for a UAV descriptor.", node->operand_count - 11);
-    }
 
     for (i = 6; i < 10; ++i)
     {
         if (!sm6_metadata_get_uint_value(sm6, node->operands[i], &values[i - 6]))
         {
-            WARN("Failed to load uint value at index %u.\n", i);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                     "A UAV descriptor operand metadata value is not an integer.");
             return VKD3D_ERROR_INVALID_SHADER;
@@ -10323,26 +10392,21 @@ static enum vkd3d_result sm6_parser_resources_load_uav(struct sm6_parser *sm6,
 static enum vkd3d_result sm6_parser_resources_load_cbv(struct sm6_parser *sm6,
         const struct sm6_metadata_node *node, struct sm6_descriptor_info *d, struct vkd3d_shader_instruction *ins)
 {
-    struct vkd3d_shader_register *reg;
+    struct vsir_operand *reg;
     unsigned int buffer_size;
 
     if (node->operand_count < 7)
     {
-        WARN("Invalid operand count %u.\n", node->operand_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
                 "Invalid operand count %u for a CBV descriptor.", node->operand_count);
         return VKD3D_ERROR_INVALID_SHADER;
     }
     if (node->operand_count > 7 && node->operands[7])
-    {
-        WARN("Ignoring %u extra operands.\n", node->operand_count - 7);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Ignoring %u extra operands for a CBV descriptor.", node->operand_count - 7);
-    }
 
     if (!sm6_metadata_get_uint_value(sm6, node->operands[6], &buffer_size))
     {
-        WARN("Failed to load buffer size.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                 "Constant buffer size metadata value is not an integer.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -10355,7 +10419,7 @@ static enum vkd3d_result sm6_parser_resources_load_cbv(struct sm6_parser *sm6,
     ins->declaration.cb.src.modifiers = VKD3DSPSM_NONE;
 
     reg = &ins->declaration.cb.src.reg;
-    vsir_register_init(reg, VKD3DSPR_CONSTBUFFER, VSIR_DATA_F32, 3);
+    vsir_operand_init(reg, VKD3DSPR_CONSTBUFFER, VSIR_DATA_F32, 3);
     reg->idx[0].offset = d->id;
     reg->idx[1].offset = d->range.first;
     reg->idx[2].offset = d->range.last;
@@ -10370,29 +10434,24 @@ static enum vkd3d_result sm6_parser_resources_load_cbv(struct sm6_parser *sm6,
 static enum vkd3d_result sm6_parser_resources_load_sampler(struct sm6_parser *sm6,
         const struct sm6_metadata_node *node, struct sm6_descriptor_info *d, struct vkd3d_shader_instruction *ins)
 {
-    struct vkd3d_shader_register *reg;
+    struct vsir_operand *reg;
     unsigned int kind;
 
     if (node->operand_count < 7)
     {
-        WARN("Invalid operand count %u.\n", node->operand_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
                 "Invalid operand count %u for a sampler descriptor.", node->operand_count);
         return VKD3D_ERROR_INVALID_SHADER;
     }
     if (node->operand_count > 7 && node->operands[7])
-    {
-        WARN("Ignoring %u extra operands.\n", node->operand_count - 7);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Ignoring %u extra operands for a sampler descriptor.", node->operand_count - 7);
-    }
 
     vsir_instruction_init(ins, &sm6->p.location, VSIR_OP_DCL_SAMPLER);
     ins->resource_type = VKD3D_SHADER_RESOURCE_NONE;
 
     if (!sm6_metadata_get_uint_value(sm6, node->operands[6], &kind))
     {
-        WARN("Failed to load sampler mode.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                 "Sampler mode metadata value is not an integer.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -10405,7 +10464,6 @@ static enum vkd3d_result sm6_parser_resources_load_sampler(struct sm6_parser *sm
             ins->flags = VKD3DSI_SAMPLER_COMPARISON_MODE;
             break;
         default:
-            FIXME("Ignoring sampler kind %u.\n", kind);
             vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                     "Ignoring sampler kind %u.", kind);
             break;
@@ -10415,7 +10473,7 @@ static enum vkd3d_result sm6_parser_resources_load_sampler(struct sm6_parser *sm
     ins->declaration.sampler.src.modifiers = VKD3DSPSM_NONE;
 
     reg = &ins->declaration.sampler.src.reg;
-    vsir_register_init(reg, VKD3DSPR_SAMPLER, VSIR_DATA_UNUSED, 3);
+    vsir_operand_init(reg, VKD3DSPR_SAMPLER, VSIR_DATA_UNUSED, 3);
     reg->idx[0].offset = d->id;
     reg->idx[1].offset = d->range.first;
     reg->idx[2].offset = d->range.last;
@@ -10443,7 +10501,6 @@ static enum vkd3d_result sm6_parser_descriptor_type_init(struct sm6_parser *sm6,
         m = descriptor_node->operands[i];
         if (!sm6_metadata_value_is_node(m))
         {
-            WARN("Resource descriptor is not a node.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                     "Resource descriptor is not a metadata node.");
             return VKD3D_ERROR_INVALID_SHADER;
@@ -10452,7 +10509,6 @@ static enum vkd3d_result sm6_parser_descriptor_type_init(struct sm6_parser *sm6,
         node = m->u.node;
         if (node->operand_count < 6)
         {
-            WARN("Invalid operand count %u.\n", node->operand_count);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
                     "Invalid operand count %u for a descriptor.", node->operand_count);
             return VKD3D_ERROR_INVALID_SHADER;
@@ -10461,7 +10517,6 @@ static enum vkd3d_result sm6_parser_descriptor_type_init(struct sm6_parser *sm6,
         if (!vkd3d_array_reserve((void **)&sm6->descriptors, &sm6->descriptor_capacity,
                 sm6->descriptor_count + 1, sizeof(*sm6->descriptors)))
         {
-            ERR("Failed to allocate descriptor array.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
                     "Out of memory allocating the descriptor array.");
             return VKD3D_ERROR_OUT_OF_MEMORY;
@@ -10471,7 +10526,6 @@ static enum vkd3d_result sm6_parser_descriptor_type_init(struct sm6_parser *sm6,
 
         if (!sm6_metadata_get_uint_value(sm6, node->operands[0], &d->id))
         {
-            WARN("Failed to load resource id.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                     "Resource id metadata value is not an integer.");
             return VKD3D_ERROR_INVALID_SHADER;
@@ -10534,7 +10588,6 @@ static enum vkd3d_result sm6_parser_resources_init(struct sm6_parser *sm6)
     node = m->u.node;
     if (node->operand_count != SHADER_DESCRIPTOR_TYPE_COUNT)
     {
-        WARN("Unexpected descriptor type count %u.\n", node->operand_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                 "Descriptor type count %u is invalid.", node->operand_count);
         return VKD3D_ERROR_INVALID_SHADER;
@@ -10547,7 +10600,6 @@ static enum vkd3d_result sm6_parser_resources_init(struct sm6_parser *sm6)
 
         if (!sm6_metadata_value_is_node(m))
         {
-            WARN("Resource list is not a node.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_RESOURCES,
                     "Resource list is not a metadata node.");
             return VKD3D_ERROR_INVALID_SHADER;
@@ -10570,7 +10622,6 @@ static void signature_element_read_additional_element_values(struct signature_el
 
     if (!sm6_metadata_value_is_node(node->operands[10]))
     {
-        WARN("Additional values list is not a node.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                 "Signature element additional values list is not a metadata node.");
         return;
@@ -10578,11 +10629,8 @@ static void signature_element_read_additional_element_values(struct signature_el
 
     node = node->operands[10]->u.node;
     if (node->operand_count & 1)
-    {
-        WARN("Operand count is not even.\n");
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Operand count for signature element additional tag/value pairs is not even.");
-    }
     operand_count = node->operand_count & ~1u;
 
     for (i = 0; i < operand_count; i += 2)
@@ -10590,7 +10638,6 @@ static void signature_element_read_additional_element_values(struct signature_el
         if (!sm6_metadata_get_uint_value(sm6, node->operands[i], &tag)
                 || !sm6_metadata_get_uint_value(sm6, node->operands[i + 1], &value))
         {
-            WARN("Failed to extract tag/value pair.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                     "Signature element tag/value pair at index %u is not an integer pair.", i);
             continue;
@@ -10604,16 +10651,12 @@ static void signature_element_read_additional_element_values(struct signature_el
             case ADDITIONAL_TAG_RELADDR_MASK:
                 /* A mask of components accessed via relative addressing. Seems to replace TPF 'dcl_index_range'. */
                 if (value > VKD3DSP_WRITEMASK_ALL)
-                {
-                    WARN("Invalid relative addressed mask %#x.\n", value);
                     vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_INVALID_MASK,
                             "Mask %#x of relative-addressed components is invalid.", value);
-                }
                 break;
             case ADDITIONAL_TAG_USED_MASK:
                 if (value > VKD3DSP_WRITEMASK_ALL)
                 {
-                    WARN("Invalid used mask %#x.\n", value);
                     vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_INVALID_MASK,
                             "Mask %#x of used components is invalid.", value);
                     value &= VKD3DSP_WRITEMASK_ALL;
@@ -10621,7 +10664,6 @@ static void signature_element_read_additional_element_values(struct signature_el
                 e->used_mask = value;
                 break;
             default:
-                FIXME("Unhandled tag %u, value %u.\n", tag, value);
                 vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                         "Tag %#x for signature element additional value %#x is unhandled.", tag, value);
                 break;
@@ -10645,7 +10687,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
 
     if (!sm6_metadata_value_is_node(m))
     {
-        WARN("Signature element list is not a node.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                 "Signature element list is not a metadata node.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -10656,20 +10697,18 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
 
     if (!(elements = vkd3d_calloc(operand_count, sizeof(*elements))))
     {
-        ERR("Failed to allocate %u signature elements.\n", operand_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
                 "Out of memory allocating %u signature elements.", operand_count);
         return VKD3D_ERROR_OUT_OF_MEMORY;
     }
 
-    native_16bit = program->global_flags & VKD3DSGF_FORCE_NATIVE_LOW_PRECISION;
+    native_16bit = program->global_flags & VKD3DSGF_ENABLE_NATIVE_LOW_PRECISION;
     for (i = 0; i < operand_count; ++i)
     {
         m = node->operands[i];
 
         if (!sm6_metadata_value_is_node(m))
         {
-            WARN("Signature element is not a node.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                     "Signature element is not a metadata node.");
             goto invalid;
@@ -10678,17 +10717,13 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
         element_node = m->u.node;
         if (element_node->operand_count < 10)
         {
-            WARN("Invalid operand count %u.\n", element_node->operand_count);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                     "Invalid signature element operand count %u.", element_node->operand_count);
             goto invalid;
         }
         if (element_node->operand_count > 11)
-        {
-            WARN("Ignoring %u extra operands.\n", element_node->operand_count - 11);
             vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                     "Ignoring %u extra operands for a signature element.", element_node->operand_count - 11);
-        }
 
         for (j = 0; j < 10; ++j)
         {
@@ -10697,7 +10732,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
                 continue;
             if (!sm6_metadata_get_uint_value(sm6, element_node->operands[j], &values[j]))
             {
-                WARN("Failed to load uint value at index %u.\n", j);
                 vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                         "Signature element value at index %u is not an integer.", j);
                 goto invalid;
@@ -10708,7 +10742,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
 
         if (values[0] != i)
         {
-            FIXME("Unsupported element id %u not equal to its index %u.\n", values[0], i);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                     "A non-sequential and non-zero-based element id is not supported.");
             goto invalid;
@@ -10716,7 +10749,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
 
         if (!sm6_metadata_value_is_string(element_node->operands[1]))
         {
-            WARN("Element name is not a string.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                     "Signature element name is not a metadata string.");
             goto invalid;
@@ -10730,7 +10762,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
         e->sysval_semantic = sysval_semantic_from_dxil_semantic_kind(j, tessellator_domain);
         if (j != SEMANTIC_KIND_ARBITRARY && j != SEMANTIC_KIND_TARGET && e->sysval_semantic == VKD3D_SHADER_SV_NONE)
         {
-            WARN("Unhandled semantic kind %u.\n", j);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                     "DXIL semantic kind %u is unhandled.", j);
             goto invalid;
@@ -10738,7 +10769,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
 
         if ((e->interpolation_mode = values[5]) >= VKD3DSIM_COUNT)
         {
-            WARN("Unhandled interpolation mode %u.\n", e->interpolation_mode);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                     "Interpolation mode %u is unhandled.", e->interpolation_mode);
             goto invalid;
@@ -10755,7 +10785,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
 
             if (register_type_from_dxil_semantic_kind(e->sysval_semantic, is_input, &dimension) == VKD3DSPR_INVALID)
             {
-                WARN("Unhandled I/O register semantic kind %u.\n", j);
                 vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                         "DXIL semantic kind %u is unhandled for an I/O register.", j);
                 goto invalid;
@@ -10763,7 +10792,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
         }
         else if (e->register_index > MAX_REG_OUTPUT || e->register_count > MAX_REG_OUTPUT - e->register_index)
         {
-            WARN("Invalid row start %u with row count %u.\n", e->register_index, e->register_count);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                     "A signature element starting row of %u with count %u is invalid.",
                     e->register_index, e->register_count);
@@ -10773,7 +10801,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
         index = values[9];
         if (index != UINT8_MAX && (index >= VKD3D_VEC4_SIZE || column_count > VKD3D_VEC4_SIZE - index))
         {
-            WARN("Invalid column start %u with count %u.\n", index, column_count);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                     "A signature element starting column %u with count %u is invalid.", index, column_count);
             goto invalid;
@@ -10796,7 +10823,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
         m = element_node->operands[4];
         if (!sm6_metadata_value_is_node(m))
         {
-            WARN("Semantic index list is not a node.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                     "Signature element semantic index list is not a metadata node.");
             goto invalid;
@@ -10807,7 +10833,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
         {
             if (!sm6_metadata_get_uint_value(sm6, element_node->operands[j], &index))
             {
-                WARN("Failed to get semantic index for row %u.\n", j);
                 vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                         "Signature element semantic index for row %u is not an integer.", j);
             }
@@ -10817,7 +10842,6 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
             }
             else if (index != e->semantic_index + j)
             {
-                WARN("Semantic index %u for row %u is not of an incrementing sequence.\n", index, j);
                 vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                         "Signature element semantic index %u for row %u is not of an incrementing sequence.", index, j);
             }
@@ -10858,7 +10882,6 @@ static enum vkd3d_result sm6_parser_signatures_init(struct sm6_parser *sm6, cons
 
     if (!sm6_metadata_value_is_node(m))
     {
-        WARN("Signature table is not a node.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_SIGNATURE,
                 "Signature table is not a metadata node.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -10876,14 +10899,6 @@ static enum vkd3d_result sm6_parser_signatures_init(struct sm6_parser *sm6, cons
             &program->patch_constant_signature, tessellator_domain, false)) < 0)
         return ret;
 
-    if (sm6->program->shader_version.type == VKD3D_SHADER_TYPE_HULL
-            && !(sm6->outpointid_param = vsir_program_create_outpointid_param(sm6->program)))
-    {
-        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
-                "Failed to allocate outpointid parameter.");
-        return VKD3D_ERROR_OUT_OF_MEMORY;
-    }
-
     if ((ret = sm6_parser_init_input_signature(sm6, &program->input_signature)) < 0)
         return ret;
 
@@ -10903,7 +10918,6 @@ static void sm6_parser_emit_global_flags(struct sm6_parser *sm6, const struct sm
 
     if (!sm6_metadata_get_uint64_value(sm6, m, (uint64_t*)&global_flags))
     {
-        WARN("Failed to load global flags.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Global flags metadata value is not an integer.");
         return;
@@ -10930,7 +10944,6 @@ static enum vkd3d_result sm6_parser_emit_thread_group(struct sm6_parser *sm6, co
 
     if (version->type != VKD3D_SHADER_TYPE_COMPUTE)
     {
-        WARN("Shader of type %#x has thread group dimensions.\n", version->type);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Shader has thread group dimensions but is not a compute shader.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -10938,7 +10951,6 @@ static enum vkd3d_result sm6_parser_emit_thread_group(struct sm6_parser *sm6, co
 
     if (!m || !sm6_metadata_value_is_node(m))
     {
-        WARN("Thread group dimension value is not a node.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Thread group dimension metadata value is not a node.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -10947,7 +10959,6 @@ static enum vkd3d_result sm6_parser_emit_thread_group(struct sm6_parser *sm6, co
     node = m->u.node;
     if (node->operand_count != 3)
     {
-        WARN("Invalid operand count %u.\n", node->operand_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
                 "Thread group dimension operand count %u is invalid.", node->operand_count);
         return VKD3D_ERROR_INVALID_SHADER;
@@ -10957,7 +10968,6 @@ static enum vkd3d_result sm6_parser_emit_thread_group(struct sm6_parser *sm6, co
     {
         if (!sm6_metadata_get_uint_value(sm6, node->operands[i], &group_sizes[i]))
         {
-            WARN("Thread group dimension is not an integer value.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                     "Thread group dimension metadata value is not an integer.");
             return VKD3D_ERROR_INVALID_SHADER;
@@ -10965,7 +10975,7 @@ static enum vkd3d_result sm6_parser_emit_thread_group(struct sm6_parser *sm6, co
         if (!group_sizes[i] || group_sizes[i] > dx_max_thread_group_size[i])
         {
             char dim = "XYZ"[i];
-            WARN("Invalid thread group %c dimension %u.\n", dim, group_sizes[i]);
+
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                     "Thread group %c dimension %u is invalid.", dim, group_sizes[i]);
             return VKD3D_ERROR_INVALID_SHADER;
@@ -11009,11 +11019,8 @@ static void sm6_parser_emit_dcl_tessellator_domain(struct sm6_parser *sm6,
     struct vkd3d_shader_instruction *ins;
 
     if (tessellator_domain == VKD3D_TESSELLATOR_DOMAIN_INVALID || tessellator_domain >= VKD3D_TESSELLATOR_DOMAIN_COUNT)
-    {
-        WARN("Unhandled domain %u.\n", tessellator_domain);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Domain shader tessellator domain %u is unhandled.", tessellator_domain);
-    }
 
     if (!(ins = sm6_parser_add_instruction(sm6, VSIR_OP_DCL_TESSELLATOR_DOMAIN)))
         return;
@@ -11025,11 +11032,8 @@ static void sm6_parser_validate_control_point_count(struct sm6_parser *sm6,
         unsigned int count, bool allow_zero, const char *type)
 {
     if ((!count && !allow_zero) || count > 32)
-    {
-        WARN("%s control point count %u invalid.\n", type, count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "%s control point count %u is invalid.", type, count);
-    }
 }
 
 static void sm6_parser_emit_dcl_tessellator_partitioning(struct sm6_parser *sm6,
@@ -11038,11 +11042,8 @@ static void sm6_parser_emit_dcl_tessellator_partitioning(struct sm6_parser *sm6,
     struct vkd3d_shader_instruction *ins;
 
     if (!tessellator_partitioning || tessellator_partitioning > VKD3D_SHADER_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN)
-    {
-        WARN("Unhandled partitioning %u.\n", tessellator_partitioning);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Hull shader tessellator partitioning %u is unhandled.", tessellator_partitioning);
-    }
 
     if (!(ins = sm6_parser_add_instruction(sm6, VSIR_OP_DCL_TESSELLATOR_PARTITIONING)))
         return;
@@ -11057,11 +11058,8 @@ static void sm6_parser_emit_dcl_tessellator_output_primitive(struct sm6_parser *
     struct vkd3d_shader_instruction *ins;
 
     if (!primitive || primitive > VKD3D_SHADER_TESSELLATOR_OUTPUT_TRIANGLE_CCW)
-    {
-        WARN("Unhandled output primitive %u.\n", primitive);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Hull shader tessellator output primitive %u is unhandled.", primitive);
-    }
 
     if (!(ins = sm6_parser_add_instruction(sm6, VSIR_OP_DCL_TESSELLATOR_OUTPUT_PRIMITIVE)))
         return;
@@ -11077,7 +11075,6 @@ static void sm6_parser_emit_dcl_max_tessellation_factor(struct sm6_parser *sm6,
 
     if (!sm6_metadata_get_float_value(sm6, m, &max_tessellation_factor))
     {
-        WARN("Max tess factor property is not a float value.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Hull shader max tessellation factor property operand is not a float.");
         return;
@@ -11085,11 +11082,8 @@ static void sm6_parser_emit_dcl_max_tessellation_factor(struct sm6_parser *sm6,
 
     /* Exclude non-finite values. */
     if (!(max_tessellation_factor >= 1.0f && max_tessellation_factor <= 64.0f))
-    {
-        WARN("Invalid max tess factor %f.\n", max_tessellation_factor);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Hull shader max tessellation factor %f is invalid.", max_tessellation_factor);
-    }
 
     if (!(ins = sm6_parser_add_instruction(sm6, VSIR_OP_DCL_HS_MAX_TESSFACTOR)))
         return;
@@ -11105,7 +11099,6 @@ static void sm6_parser_gs_properties_init(struct sm6_parser *sm6, const struct s
 
     if (!m || !sm6_metadata_value_is_node(m))
     {
-        WARN("Missing or invalid GS properties.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Geometry shader properties node is missing or invalid.");
         return;
@@ -11114,27 +11107,20 @@ static void sm6_parser_gs_properties_init(struct sm6_parser *sm6, const struct s
     node = m->u.node;
     if (node->operand_count < ARRAY_SIZE(operands))
     {
-        WARN("Invalid operand count %u.\n", node->operand_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
                 "Geometry shader properties operand count %u is invalid.", node->operand_count);
         return;
     }
     if (node->operand_count > ARRAY_SIZE(operands))
-    {
-        WARN("Ignoring %zu extra operands.\n", node->operand_count - ARRAY_SIZE(operands));
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Ignoring %zu extra operands for geometry shader properties.",
                 node->operand_count - ARRAY_SIZE(operands));
-    }
 
     for (i = 0; i < node->operand_count; ++i)
     {
         if (!sm6_metadata_get_uint_value(sm6, node->operands[i], &operands[i]))
-        {
-            WARN("GS property at index %u is not a uint value.\n", i);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                     "Geometry shader properties operand at index %u is not an integer.", i);
-        }
     }
 
     switch (i = operands[0])
@@ -11173,7 +11159,6 @@ static void sm6_parser_gs_properties_init(struct sm6_parser *sm6, const struct s
                 break;
             }
 
-            WARN("Unhandled input primitive %u.\n", i);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                     "Geometry shader input primitive %u is unhandled.", i);
             break;
@@ -11186,25 +11171,18 @@ static void sm6_parser_gs_properties_init(struct sm6_parser *sm6, const struct s
     i = operands[1];
     /* Max total scalar count sets an upper limit. We would need to scan outputs to be more precise. */
     if (i > MAX_GS_OUTPUT_TOTAL_SCALARS)
-    {
-        WARN("GS output vertex count %u invalid.\n", i);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Geometry shader output vertex count %u is invalid.", i);
-    }
     sm6_parser_emit_dcl_count(sm6, VSIR_OP_DCL_VERTICES_OUT, i);
     sm6->program->vertices_out_count = i;
 
     if (operands[2] > 1)
-    {
-        FIXME("Unhandled stream mask %#x.\n", operands[2]);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Geometry shader stream mask %#x is unhandled.", operands[2]);
-    }
 
     output_primitive = operands[3];
     if (output_primitive == VKD3D_PT_UNDEFINED || output_primitive >= VKD3D_PT_COUNT)
     {
-        WARN("Unhandled output primitive %u.\n", output_primitive);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Geometry shader output primitive %u is unhandled.", output_primitive);
         output_primitive = VKD3D_PT_TRIANGLELIST;
@@ -11214,11 +11192,8 @@ static void sm6_parser_gs_properties_init(struct sm6_parser *sm6, const struct s
 
     i = operands[4];
     if (!i || i > MAX_GS_INSTANCE_COUNT)
-    {
-        WARN("GS instance count %u invalid.\n", i);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Geometry shader instance count %u is invalid.", i);
-    }
     sm6_parser_emit_dcl_count(sm6, VSIR_OP_DCL_GS_INSTANCES, i);
 }
 
@@ -11231,7 +11206,6 @@ static enum vkd3d_tessellator_domain sm6_parser_ds_properties_init(struct sm6_pa
 
     if (!m || !sm6_metadata_value_is_node(m))
     {
-        WARN("Missing or invalid DS properties.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Domain shader properties node is missing or invalid.");
         return 0;
@@ -11240,27 +11214,20 @@ static enum vkd3d_tessellator_domain sm6_parser_ds_properties_init(struct sm6_pa
     node = m->u.node;
     if (node->operand_count < ARRAY_SIZE(operands))
     {
-        WARN("Invalid operand count %u.\n", node->operand_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
                 "Domain shader properties operand count %u is invalid.", node->operand_count);
         return 0;
     }
     if (node->operand_count > ARRAY_SIZE(operands))
-    {
-        WARN("Ignoring %zu extra operands.\n", node->operand_count - ARRAY_SIZE(operands));
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Ignoring %zu extra operands for domain shader properties.",
                 node->operand_count - ARRAY_SIZE(operands));
-    }
 
     for (i = 0; i < node->operand_count; ++i)
     {
         if (!sm6_metadata_get_uint_value(sm6, node->operands[i], &operands[i]))
-        {
-            WARN("DS property at index %u is not a uint value.\n", i);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                     "Domain shader properties operand at index %u is not an integer.", i);
-        }
     }
 
     sm6_parser_emit_dcl_tessellator_domain(sm6, operands[0]);
@@ -11280,7 +11247,6 @@ static enum vkd3d_tessellator_domain sm6_parser_hs_properties_init(struct sm6_pa
 
     if (!m || !sm6_metadata_value_is_node(m))
     {
-        WARN("Missing or invalid HS properties.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Hull shader properties node is missing or invalid.");
         return 0;
@@ -11289,38 +11255,26 @@ static enum vkd3d_tessellator_domain sm6_parser_hs_properties_init(struct sm6_pa
     node = m->u.node;
     if (node->operand_count < 7)
     {
-        WARN("Invalid operand count %u.\n", node->operand_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
                 "Hull shader properties operand count %u is invalid.", node->operand_count);
         return 0;
     }
     if (node->operand_count > 7)
-    {
-        WARN("Ignoring %u extra operands.\n", node->operand_count - 7);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                 "Ignoring %u extra operands for hull shader properties.", node->operand_count - 7);
-    }
 
     m = node->operands[0];
     if (!sm6_metadata_value_is_value(m) || !sm6_value_is_function_dcl(m->u.value))
-    {
-        WARN("Patch constant function node is not a function value.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                 "Hull shader patch constant function node is not a function value.");
-    }
     else
-    {
         sm6->patch_constant_function = m->u.value->u.function.name;
-    }
 
     for (i = 1; i < min(node->operand_count, ARRAY_SIZE(operands)); ++i)
     {
         if (!sm6_metadata_get_uint_value(sm6, node->operands[i], &operands[i]))
-        {
-            WARN("HS property at index %u is not a uint value.\n", i);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                     "Hull shader properties operand at index %u is not an integer.", i);
-        }
     }
 
     sm6_parser_validate_control_point_count(sm6, operands[1], false, "Hull shader input");
@@ -11347,7 +11301,6 @@ static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
 
     if (!entry_node || entry_node->operand_count < 2 || !(m = entry_node->operands[0]))
     {
-        WARN("No entry point definition found.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_ENTRY_POINT,
                 "No entry point definition found in the metadata.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -11355,7 +11308,6 @@ static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
 
     if (m->type != VKD3D_METADATA_VALUE)
     {
-        WARN("Entry point definition is not a value.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_ENTRY_POINT,
                 "Entry point definition is not a metadata value.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -11364,7 +11316,6 @@ static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
     value = m->u.value;
     if (!sm6_value_is_function_dcl(value))
     {
-        WARN("Entry point value is not a function definition.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_ENTRY_POINT,
                 "Entry point metadata value does not contain a function definition.");
         return VKD3D_ERROR_INVALID_SHADER;
@@ -11373,17 +11324,13 @@ static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
     sm6->entry_point = value->u.function.name;
     if (!sm6_metadata_value_is_string(entry_node->operands[1])
             || strcmp(sm6->entry_point, entry_node->operands[1]->u.string_value))
-    {
-        WARN("Entry point function name %s mismatch.\n", sm6->entry_point);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_ENTRY_POINT_MISMATCH,
                 "Entry point function name %s does not match the name in metadata.", sm6->entry_point);
-    }
 
     if (entry_node->operand_count >= 5 && (m = entry_node->operands[4]))
     {
         if (!sm6_metadata_value_is_node(m))
         {
-            WARN("Shader properties list is not a node.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                     "Shader properties tag/value list is not a metadata node.");
             return VKD3D_ERROR_INVALID_SHADER;
@@ -11391,18 +11338,14 @@ static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
 
         node = m->u.node;
         if (node->operand_count & 1)
-        {
-            WARN("Operand count is not even.\n");
             vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
                     "Operand count for shader properties tag/value pairs is not even.");
-        }
         operand_count = node->operand_count & ~1u;
 
         for (i = 0; i < operand_count; i += 2)
         {
             if (!sm6_metadata_get_uint_value(sm6, node->operands[i], &tag))
             {
-                WARN("Tag is not an integer value.\n");
                 vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                         "Shader properties tag at index %u is not an integer.", i);
                 return VKD3D_ERROR_INVALID_SHADER;
@@ -11427,7 +11370,6 @@ static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
                         return ret;
                     break;
                 default:
-                    FIXME("Unhandled tag %#x.\n", tag);
                     vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
                             "Shader properties tag %#x is unhandled.", tag);
                     break;
@@ -11552,8 +11494,14 @@ static void sm6_parser_cleanup_attribute_groups(struct sm6_parser *dxil)
     vkd3d_free(dxil->attribute_groups);
 }
 
+static void dxil_cleanup_fixups(struct sm6_parser *dxil)
+{
+    vkd3d_free(dxil->fixups);
+}
+
 static void sm6_parser_cleanup(struct sm6_parser *sm6)
 {
+    dxil_cleanup_fixups(sm6);
     dxil_block_destroy(&sm6->root_block);
     dxil_global_abbrevs_cleanup(sm6->abbrevs, sm6->abbrev_count);
     sm6_type_table_cleanup(sm6->types, sm6->type_count);
@@ -11566,7 +11514,7 @@ static void sm6_parser_cleanup(struct sm6_parser *sm6)
     vkd3d_free(sm6->values);
 }
 
-static enum vsir_denorm_mode sm6_function_get_denorm_mode(const struct sm6_function *function,
+static enum vkd3d_shader_denormal_mode sm6_function_get_denorm_mode(const struct sm6_function *function,
         struct sm6_parser *dxil)
 {
     unsigned int attribs_id = function->declaration->u.function.attribs_id;
@@ -11574,13 +11522,13 @@ static enum vsir_denorm_mode sm6_function_get_denorm_mode(const struct sm6_funct
     size_t i, j, k;
 
     if (!attribs_id)
-        return VSIR_DENORM_FLUSH_TO_ZERO;
+        return VKD3D_SHADER_DENORMAL_MODE_FLUSH_TO_ZERO;
 
     if (attribs_id > dxil->parameter_attribute_count)
     {
         vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_ATTRIBUTE,
                 "Invalid attribute id %u.", attribs_id);
-        return VSIR_DENORM_FLUSH_TO_ZERO;
+        return VKD3D_SHADER_DENORMAL_MODE_FLUSH_TO_ZERO;
     }
 
     parameter_attribute = &dxil->parameter_attributes[attribs_id - 1];
@@ -11604,22 +11552,22 @@ static enum vsir_denorm_mode sm6_function_get_denorm_mode(const struct sm6_funct
                     continue;
 
                 if (!strcmp(attribute->value.string, "preserve"))
-                    return VSIR_DENORM_PRESERVE;
+                    return VKD3D_SHADER_DENORMAL_MODE_PRESERVE;
 
                 if (!strcmp(attribute->value.string, "ftz"))
-                    return VSIR_DENORM_FLUSH_TO_ZERO;
+                    return VKD3D_SHADER_DENORMAL_MODE_FLUSH_TO_ZERO;
 
                 if (!strcmp(attribute->value.string, "any"))
-                    return VSIR_DENORM_ANY;
+                    return VKD3D_SHADER_DENORMAL_MODE_ANY;
 
                 vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_ATTRIBUTE,
                         "Invalid value for attribute `fp32-denorm-mode'.");
-                return VSIR_DENORM_FLUSH_TO_ZERO;
+                return VKD3D_SHADER_DENORMAL_MODE_FLUSH_TO_ZERO;
             }
         }
     }
 
-    return VSIR_DENORM_FLUSH_TO_ZERO;
+    return VKD3D_SHADER_DENORMAL_MODE_FLUSH_TO_ZERO;
 }
 
 static struct sm6_function *sm6_parser_get_function(const struct sm6_parser *sm6, const char *name)
@@ -11650,7 +11598,6 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
     count = byte_code_size / sizeof(*byte_code);
     if (count < 6)
     {
-        WARN("Invalid data size %zu.\n", byte_code_size);
         vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_SIZE,
                 "DXIL chunk size %zu is smaller than the DXIL header size.", byte_code_size);
         return VKD3D_ERROR_INVALID_SHADER;
@@ -11663,7 +11610,6 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
 
     if (token_count < 6 || count < token_count)
     {
-        WARN("Invalid token count %u (word count %zu).\n", token_count, count);
         vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_SIZE,
                 "DXIL chunk token count %#x is invalid (word count %zu).", token_count, count);
         return VKD3D_ERROR_INVALID_SHADER;
@@ -11681,7 +11627,6 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
     chunk_offset = byte_code[4];
     if (chunk_offset < 16 || chunk_offset >= byte_code_size)
     {
-        WARN("Invalid bitcode chunk offset %#x (data size %zu).\n", chunk_offset, byte_code_size);
         vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_OFFSET,
                 "DXIL bitcode chunk has invalid offset %#x (data size %#zx).", chunk_offset, byte_code_size);
         return VKD3D_ERROR_INVALID_SHADER;
@@ -11689,8 +11634,6 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
     chunk_size = byte_code[5];
     if (chunk_size > byte_code_size - chunk_offset)
     {
-        WARN("Invalid bitcode chunk size %#x (data size %zu, chunk offset %#x).\n",
-                chunk_size, byte_code_size, chunk_offset);
         vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_SIZE,
                 "DXIL bitcode chunk has invalid size %#x (data size %#zx, chunk offset %#x).",
                 chunk_size, byte_code_size, chunk_offset);
@@ -11699,27 +11642,20 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
 
     sm6->start = (const uint32_t *)((const char*)&byte_code[2] + chunk_offset);
     if ((magic = sm6->start[0]) != BITCODE_MAGIC)
-    {
-        WARN("Unknown magic number 0x%08x.\n", magic);
         vkd3d_shader_warning(message_context, &location, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_MAGIC_NUMBER,
                 "DXIL bitcode chunk magic number 0x%08x is not the expected 0x%08x.", magic, BITCODE_MAGIC);
-    }
 
     sm6->end = &sm6->start[(chunk_size + sizeof(*sm6->start) - 1) / sizeof(*sm6->start)];
 
     if ((version.type = version_token >> 16) >= VKD3D_SHADER_TYPE_COUNT)
-    {
-        FIXME("Unknown shader type %#x.\n", version.type);
         vkd3d_shader_warning(message_context, &location, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_SHADER_TYPE,
                 "Unknown shader type %#x.", version.type);
-    }
 
     version.major = VKD3D_SM6_VERSION_MAJOR(version_token);
     version.minor = VKD3D_SM6_VERSION_MINOR(version_token);
 
     if ((abbr = sm6->start[1] & 3) != ENTER_SUBBLOCK)
     {
-        WARN("Initial block abbreviation %u is not ENTER_SUBBLOCK.\n", abbr);
         vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_BITCODE,
                 "DXIL bitcode chunk has invalid initial block abbreviation %u.", abbr);
         return VKD3D_ERROR_INVALID_SHADER;
@@ -11773,16 +11709,12 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
 
     length = sm6->ptr - sm6->start - block->start;
     if (length != block->length)
-    {
-        WARN("Invalid block length %zu; expected %u.\n", length, block->length);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_INVALID_BLOCK_LENGTH,
                 "Root block ends with length %zu but indicated length is %u.", length, block->length);
-    }
     if (sm6->ptr != sm6->end)
     {
         size_t expected_length = sm6->end - sm6->start;
         length = sm6->ptr - sm6->start;
-        WARN("Invalid module length %zu; expected %zu.\n", length, expected_length);
         vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_INVALID_MODULE_LENGTH,
                 "Module ends with length %zu but indicated length is %zu.", length, expected_length);
     }
@@ -11816,7 +11748,6 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
     function_count = dxil_block_compute_function_count(&sm6->root_block);
     if (!(sm6->functions = vkd3d_calloc(function_count, sizeof(*sm6->functions))))
     {
-        ERR("Failed to allocate function array.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
                 "Out of memory allocating DXIL function array.");
         ret = VKD3D_ERROR_OUT_OF_MEMORY;
@@ -11825,7 +11756,6 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
 
     if (sm6_parser_compute_max_value_count(sm6, &sm6->root_block, 0) == SIZE_MAX)
     {
-        WARN("Value array count overflowed.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE,
                 "Overflow occurred in the DXIL module value count.");
         ret = VKD3D_ERROR_INVALID_SHADER;
@@ -11833,7 +11763,6 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
     }
     if (!(sm6->values = vkd3d_calloc(sm6->value_capacity, sizeof(*sm6->values))))
     {
-        ERR("Failed to allocate value array.\n");
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
                 "Out of memory allocating DXIL value array.");
         ret = VKD3D_ERROR_OUT_OF_MEMORY;
@@ -11863,7 +11792,6 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
 
         if (j == ARRAY_SIZE(sm6->metadata_tables))
         {
-            FIXME("Too many metadata tables.\n");
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA,
                     "A metadata table count greater than %zu is unsupported.", ARRAY_SIZE(sm6->metadata_tables));
             ret = VKD3D_ERROR_INVALID_SHADER;
@@ -11895,18 +11823,17 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
 
     if (!(fn = sm6_parser_get_function(sm6, sm6->entry_point)))
     {
-        WARN("Failed to find entry point %s.\n", sm6->entry_point);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_ENTRY_POINT,
                 "The definition of the entry point function '%s' was not found.", sm6->entry_point);
         ret = VKD3D_ERROR_INVALID_SHADER;
         goto fail;
     }
 
-    program->f32_denorm_mode = sm6_function_get_denorm_mode(fn, sm6);
+    program->f32_denormal_mode = sm6_function_get_denorm_mode(fn, sm6);
 
     if (version.type == VKD3D_SHADER_TYPE_HULL)
     {
-        enum vsir_denorm_mode cp_denorm_mode;
+        enum vkd3d_shader_denormal_mode cp_denorm_mode;
 
         sm6_parser_add_instruction(sm6, VSIR_OP_HS_CONTROL_POINT_PHASE);
 
@@ -11915,7 +11842,6 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
 
         if (!(fn = sm6_parser_get_function(sm6, sm6->patch_constant_function)))
         {
-            WARN("Failed to find patch constant function '%s'.\n", sm6->patch_constant_function);
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE,
                     "Failed to find the patch constant function '%s' for a hull shader.",
                     sm6->patch_constant_function);
@@ -11925,11 +11851,11 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
 
         cp_denorm_mode = sm6_function_get_denorm_mode(fn, sm6);
 
-        if (sm6->p.status >= 0 && program->f32_denorm_mode != cp_denorm_mode)
+        if (sm6->p.status >= 0 && program->f32_denormal_mode != cp_denorm_mode)
         {
             vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_ATTRIBUTE,
                     "Patch constant denorm mode %u doesn't match control point denorm mode %u.",
-                    program->f32_denorm_mode, cp_denorm_mode);
+                    program->f32_denormal_mode, cp_denorm_mode);
         }
 
         sm6_parser_add_instruction(sm6, VSIR_OP_HS_FORK_PHASE);
@@ -11946,11 +11872,8 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, struct vsir_pro
     }
 
     if (sm6->function_count > expected_function_count)
-    {
-        FIXME("%zu unhandled functions.\n", sm6->function_count - expected_function_count);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE,
                 "%zu functions were not emitted.", sm6->function_count - expected_function_count);
-    }
 
     dxil_block_destroy(&sm6->root_block);
 
diff --git a/libs/vkd3d/libs/vkd3d-shader/fx.c b/libs/vkd3d/libs/vkd3d-shader/fx.c
index 1eb8d8d44b20..5140513b3463 100644
--- a/libs/vkd3d/libs/vkd3d-shader/fx.c
+++ b/libs/vkd3d/libs/vkd3d-shader/fx.c
@@ -1303,6 +1303,15 @@ static const char * get_fx_4_type_name(const struct hlsl_type *type)
         case HLSL_CLASS_PIXEL_SHADER:
             return "PixelShader";
 
+        case HLSL_CLASS_HULL_SHADER:
+            return "HullShader";
+
+        case HLSL_CLASS_DOMAIN_SHADER:
+            return "DomainShader";
+
+        case HLSL_CLASS_COMPUTE_SHADER:
+            return "ComputeShader";
+
         case HLSL_CLASS_STRING:
             return "String";
 
@@ -2417,7 +2426,10 @@ static uint32_t write_shader_blob(const struct hlsl_ir_compile *compile, struct
     ret = hlsl_emit_vsir(ctx, &compile_info, compile->decl, &compile->initializers, &program, &rdef);
     ctx->profile = profile;
     if (ret < 0)
+    {
+        vsir_program_cleanup(&program);
         return 0;
+    }
 
     ret = vsir_program_compile(&program, &rdef, vkd3d_shader_init_config_flags(),
             &compile_info, &source, ctx->message_context);
@@ -3847,13 +3859,15 @@ static void fx_parser_skip(struct fx_parser *parser, size_t size)
     parser->ptr += size;
 }
 
-static void VKD3D_PRINTF_FUNC(3, 4) fx_parser_error(struct fx_parser *parser, enum vkd3d_shader_error error,
-        const char *format, ...)
+#define fx_parser_error(parser, error, ...) \
+        fx_parser_error_(parser, error, __FUNCTION__, __VA_ARGS__)
+static void VKD3D_PRINTF_FUNC(4, 5) fx_parser_error_(struct fx_parser *parser, enum vkd3d_shader_error error,
+        const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_verror(parser->message_context, NULL, error, format, args);
+    vkd3d_shader_verror(parser->message_context, NULL, error, function, format, args);
     va_end(args);
 
     parser->failed = true;
@@ -5023,6 +5037,7 @@ fxlc_opcodes[] =
     { 0x216, "iadd"  },
     { 0x219, "imul"  },
     { 0x21a, "udiv"  },
+    { 0x21b, "umod"  },
     { 0x21d, "imin"  },
     { 0x21e, "imax"  },
     { 0x21f, "umin"  },
@@ -5066,19 +5081,18 @@ struct fx_4_ctab_entry
 
 struct fxlc_arg
 {
-    uint32_t reg_type;
-    uint32_t address;
-    bool indexed;
-    struct
+    uint32_t index_count;
+    struct fxlc_register
     {
-        uint32_t reg_type;
+        uint32_t type;
         uint32_t address;
-    } index;
+    } registers[/* .index_count + 1 */];
 };
 
 struct fxlvm_code
 {
-    const uint32_t *ptr, *end;
+    const uint32_t *code;
+    size_t pos, size;
     bool failed;
 
     union
@@ -5099,13 +5113,35 @@ struct fxlvm_code
 
 static uint32_t fxlvm_read_u32(struct fxlvm_code *code)
 {
-    if (code->end == code->ptr)
+    if (code->pos >= code->size)
     {
         code->failed = true;
         return 0;
     }
 
-    return *code->ptr++;
+    return code->code[code->pos++];
+}
+
+static bool fxlvm_skip(struct fxlvm_code *code, size_t count)
+{
+    if (code->size - code->pos < count)
+    {
+        code->pos = code->size;
+        code->failed = true;
+        return false;
+    }
+
+    code->pos += count;
+    return true;
+}
+
+static const struct fxlc_arg *fxlvm_read_argument(struct fxlvm_code *code)
+{
+    const void *ptr = &code->code[code->pos];
+    size_t indices;
+
+    indices = fxlvm_read_u32(code) + 1;
+    return fxlvm_skip(code, indices * 2) ? ptr : NULL;
 }
 
 static const uint32_t *find_d3dbc_section(const uint32_t *ptr, uint32_t count, uint32_t tag, uint32_t *size)
@@ -5144,8 +5180,7 @@ static void fx_parse_print_swizzle(struct fx_parser *parser, const struct fxlvm_
         vkd3d_string_buffer_printf(&parser->buffer, ".%.*s", comp_count, &comp[addr % 4]);
 }
 
-static void fx_print_fxlc_register(struct fx_parser *parser, uint32_t reg_type,
-        uint32_t address, uint32_t index_type, uint32_t index_address, struct fxlvm_code *code)
+static void fx_print_fxlc_register(struct fx_parser *parser, const struct fxlc_register *reg, struct fxlvm_code *code)
 {
     static const char *table_names[FX_FXLC_REG_MAX + 1] =
     {
@@ -5155,12 +5190,12 @@ static void fx_print_fxlc_register(struct fx_parser *parser, uint32_t reg_type,
         [FX_FXLC_REG_OUTPUT] = "expr",
         [FX_FXLC_REG_TEMP] = "r",
     };
-    uint32_t reg_index = address / 4;
+    uint32_t reg_index = reg->address / 4;
 
     if (parser->source_type == VKD3D_SHADER_SOURCE_TX
-            && (reg_type == FX_FXLC_REG_INPUT || reg_type == FX_FXLC_REG_OUTPUT))
+            && (reg->type == FX_FXLC_REG_INPUT || reg->type == FX_FXLC_REG_OUTPUT))
     {
-        if (reg_type == FX_FXLC_REG_INPUT)
+        if (reg->type == FX_FXLC_REG_INPUT)
         {
             if (reg_index == 0)
                 vkd3d_string_buffer_printf(&parser->buffer, "vPos");
@@ -5174,67 +5209,29 @@ static void fx_print_fxlc_register(struct fx_parser *parser, uint32_t reg_type,
     }
     else
     {
-        vkd3d_string_buffer_printf(&parser->buffer, "%s%u", table_names[reg_type], reg_index);
-    }
-    if (index_type != FX_FXLC_REG_UNUSED)
-    {
-        vkd3d_string_buffer_printf(&parser->buffer, "[%s%u.%c]", table_names[index_type],
-                index_address / 4, "xyzw"[index_address % 4]);
+        vkd3d_string_buffer_printf(&parser->buffer, "%s%u", table_names[reg->type], reg_index);
     }
-    fx_parse_print_swizzle(parser, code, address);
 }
 
-static void fx_parse_fxlc_constant_argument(struct fx_parser *parser,
-        const struct fxlc_arg *arg, const struct fxlvm_code *code)
+static const struct fx_4_ctab_entry *find_register_entry(const struct fxlvm_code *code, uint32_t register_index)
 {
-    uint32_t register_index = arg->address / 4; /* Address counts in components. */
+    if (!code->ctab)
+        return NULL;
 
-    if (code->ctab_count)
+    for (size_t i = 0; i < code->ctab_count; ++i)
     {
-        uint32_t i, offset;
+        const struct fx_4_ctab_entry *entry = &code->constants[i];
 
-        for (i = 0; i < code->ctab_count; ++i)
-        {
-            const struct fx_4_ctab_entry *c = &code->constants[i];
-
-            if (register_index < c->register_index || register_index - c->register_index >= c->register_count)
-                continue;
-
-            vkd3d_string_buffer_printf(&parser->buffer, "%s", &code->ctab[c->name]);
-
-            /* Register offset within variable */
-            offset = arg->address - c->register_index * 4;
+        if (register_index < entry->register_index)
+            continue;
 
-            if (offset / 4)
-                vkd3d_string_buffer_printf(&parser->buffer, "[%u]", offset / 4);
-            fx_parse_print_swizzle(parser, code, offset);
-            return;
-        }
+        if (register_index >= entry->register_index + entry->register_count)
+            continue;
 
-        vkd3d_string_buffer_printf(&parser->buffer, "(var-not-found)");
+        return entry;
     }
-    else
-    {
-        vkd3d_string_buffer_printf(&parser->buffer, "c%u", register_index);
-        fx_parse_print_swizzle(parser, code, arg->address);
-    }
-}
-
-static void fx_parse_fxlc_argument(struct fx_parser *parser, struct fxlc_arg *arg, struct fxlvm_code *code)
-{
-    uint32_t flags;
 
-    memset(arg, 0, sizeof(*arg));
-
-    flags = fxlvm_read_u32(code);
-    if (flags)
-    {
-        arg->indexed = true;
-        arg->index.reg_type = fxlvm_read_u32(code);
-        arg->index.address  = fxlvm_read_u32(code);
-    }
-    arg->reg_type = fxlvm_read_u32(code);
-    arg->address  = fxlvm_read_u32(code);
+    return NULL;
 }
 
 static void fx_print_fxlc_literal(struct fx_parser *parser, uint32_t address, struct fxlvm_code *code)
@@ -5247,69 +5244,126 @@ static void fx_print_fxlc_literal(struct fx_parser *parser, uint32_t address, st
 
 static void fx_print_fxlc_argument(struct fx_parser *parser, const struct fxlc_arg *arg, struct fxlvm_code *code)
 {
-    uint32_t count;
+    bool pending_brace = false;
+    unsigned index_level = 0;
 
-    if (arg->reg_type > FX_FXLC_REG_MAX)
+    for (size_t i = 0; i <= arg->index_count; ++i)
     {
-        fx_parser_error(parser, VKD3D_SHADER_ERROR_FX_INVALID_DATA,
-                "Unexpected register type %u.", arg->reg_type);
-        return;
-    }
+        /* register index chain goes outside in, so start at the end. */
+        const struct fxlc_register *reg = &arg->registers[arg->index_count - i];
 
-    if (arg->index.reg_type > FX_FXLC_REG_MAX)
-    {
-        fx_parser_error(parser, VKD3D_SHADER_ERROR_FX_INVALID_DATA,
-                "Unexpected index register type %u.", arg->index.reg_type);
-        return;
-    }
+        if (reg->type > FX_FXLC_REG_MAX)
+        {
+            fx_parser_error(parser, VKD3D_SHADER_ERROR_FX_INVALID_DATA,
+                    "Unexpected register type %u.", reg->type);
+            return;
+        }
 
-    if (arg->indexed)
-    {
-        fx_print_fxlc_register(parser, arg->reg_type, arg->address, arg->index.reg_type,
-                arg->index.address, code);
-        return;
-    }
+        if (index_level++)
+        {
+            vkd3d_string_buffer_printf(&parser->buffer, pending_brace ? " + " : "[");
+            pending_brace = false;
+        }
 
-    switch (arg->reg_type)
-    {
-        case FX_FXLC_REG_LITERAL:
-            count = code->scalar ? 1 : code->comp_count;
-            if (arg->address >= code->cli_count || count > code->cli_count - arg->address)
+        switch (reg->type)
+        {
+            case FX_FXLC_REG_LITERAL:
             {
-                vkd3d_string_buffer_printf(&parser->buffer, "(<out-of-bounds>)");
-                parser->failed = true;
+                uint32_t count = code->scalar ? 1 : code->comp_count;
+
+                if (reg->address >= code->cli_count || count > code->cli_count - reg->address)
+                {
+                    vkd3d_string_buffer_printf(&parser->buffer, "(<out-of-bounds>)");
+                    parser->failed = true;
+                    break;
+                }
+
+                vkd3d_string_buffer_printf(&parser->buffer, "(");
+                fx_print_fxlc_literal(parser, reg->address, code);
+                for (unsigned int j = 1; j < code->comp_count; ++j)
+                {
+                    vkd3d_string_buffer_printf(&parser->buffer, ", ");
+                    fx_print_fxlc_literal(parser, reg->address + (code->scalar ? 0 : j), code);
+                }
+                vkd3d_string_buffer_printf(&parser->buffer, ")");
                 break;
             }
-
-            vkd3d_string_buffer_printf(&parser->buffer, "(");
-            fx_print_fxlc_literal(parser, arg->address, code);
-            for (unsigned int i = 1; i < code->comp_count; ++i)
+            case FX_FXLC_REG_CB:
             {
-                vkd3d_string_buffer_printf(&parser->buffer, ", ");
-                fx_print_fxlc_literal(parser, arg->address + (code->scalar ? 0 : i), code);
+                uint32_t register_index = reg->address / 4; /* Address counts in components. */
+                const struct fx_4_ctab_entry *entry;
+
+                if (!(entry = find_register_entry(code, register_index)))
+                {
+                    vkd3d_string_buffer_printf(&parser->buffer, "c%u", register_index);
+                    break;
+                }
+
+                vkd3d_string_buffer_printf(&parser->buffer, "%s", &code->ctab[entry->name]);
+                if (register_index -= entry->register_index)
+                {
+                    vkd3d_string_buffer_printf(&parser->buffer, "[%d", register_index);
+                    pending_brace = true;
+                }
+                break;
             }
-            vkd3d_string_buffer_printf(&parser->buffer, ")");
-            break;
 
-        case FX_FXLC_REG_CB:
-            fx_parse_fxlc_constant_argument(parser, arg, code);
-            break;
+            case FX_FXLC_REG_OUTPUT:
+                vkd3d_string_buffer_printf(&parser->buffer, "expr");
+                break;
 
-        case FX_FXLC_REG_INPUT:
-        case FX_FXLC_REG_OUTPUT:
-        case FX_FXLC_REG_TEMP:
-            fx_print_fxlc_register(parser, arg->reg_type, arg->address, FX_FXLC_REG_UNUSED, 0, code);
-            break;
+            case FX_FXLC_REG_INPUT:
+            case FX_FXLC_REG_TEMP:
+                fx_print_fxlc_register(parser, reg, code);
+                break;
 
-        default:
-            vkd3d_string_buffer_printf(&parser->buffer, "<unknown register %u>", arg->reg_type);
-            break;
+            default:
+                vkd3d_string_buffer_printf(&parser->buffer, "<unknown register %u>", reg->type);
+                break;
+        }
+    }
+
+    for (size_t i = 0; i <= arg->index_count; ++i)
+    {
+        const struct fxlc_register *reg = &arg->registers[i];
+
+        if (pending_brace)
+        {
+            vkd3d_string_buffer_printf(&parser->buffer, "]");
+            pending_brace = false;
+        }
+
+        switch (reg->type)
+        {
+            case FX_FXLC_REG_LITERAL:
+                break;
+            case FX_FXLC_REG_CB:
+            {
+                const struct fx_4_ctab_entry *entry;
+
+                if ((entry = find_register_entry(code, reg->address / 4)))
+                {
+                    fx_parse_print_swizzle(parser, code, reg->address - entry->register_index * 4);
+                    break;
+                }
+            }
+            /* fall-through */
+            default:
+                fx_parse_print_swizzle(parser, code, reg->address);
+                break;
+        }
+
+        if (--index_level)
+        {
+            vkd3d_string_buffer_printf(&parser->buffer, "]");
+            pending_brace = false;
+        }
     }
 }
 
 static void fx_parse_fxlvm_expression(struct fx_parser *parser, struct fxlvm_code *code)
 {
-    struct fxlc_arg args[9];
+    const struct fxlc_arg *args[9];
     uint32_t ins_count;
     size_t i, j;
 
@@ -5338,9 +5392,8 @@ static void fx_parse_fxlvm_expression(struct fx_parser *parser, struct fxlvm_cod
 
         /* Sources entries are followed by the destination, first read them all.
            Output format is "opcode dst, src[0]...src[n]". */
-        for (j = 0; j < src_count; ++j)
-            fx_parse_fxlc_argument(parser, &args[j], code);
-        fx_parse_fxlc_argument(parser, &args[src_count], code);
+        for (j = 0; j <= src_count; ++j)
+            args[j] = fxlvm_read_argument(code);
 
         opcode = (instr >> FX_FXLC_OPCODE_SHIFT) & FX_FXLC_OPCODE_MASK;
         code->comp_count = instr & FX_FXLC_COMP_COUNT_MASK;
@@ -5349,14 +5402,14 @@ static void fx_parse_fxlvm_expression(struct fx_parser *parser, struct fxlvm_cod
         vkd3d_string_buffer_printf(&parser->buffer, "%s ", get_fxlc_opcode_name(opcode));
 
         code->scalar = false;
-        fx_print_fxlc_argument(parser, &args[src_count], code);
+        fx_print_fxlc_argument(parser, args[src_count], code);
         vkd3d_string_buffer_printf(&parser->buffer, ", ");
 
         for (j = 0; j < src_count; ++j)
         {
             /* Scalar modifier applies only to the first source. */
             code->scalar = j == 0 && !!(instr & FX_FXLC_IS_SCALAR_MASK);
-            fx_print_fxlc_argument(parser, &args[j], code);
+            fx_print_fxlc_argument(parser, args[j], code);
             if (j < src_count - 1)
                 vkd3d_string_buffer_printf(&parser->buffer, ", ");
         }
@@ -5387,10 +5440,10 @@ static void fx_2_parse_fxlvm_expression(struct fx_parser *parser, const uint32_t
     /* CTAB does not contain variable names */
 
     /* Code blob */
-    code.ptr = find_d3dbc_section(blob, count, TAG_FXLC, &count);
-    code.end = code.ptr + count;
+    code.code = find_d3dbc_section(blob, count, TAG_FXLC, &count);
+    code.size = count;
 
-    if (!code.ptr)
+    if (!code.code)
     {
         fx_parser_error(parser, VKD3D_SHADER_ERROR_FX_INVALID_DATA, "Failed to locate expression code section.");
         return;
@@ -5459,8 +5512,8 @@ static void fx_4_parse_fxlvm_expression(struct fx_parser *parser, uint32_t offse
                 ctab_offset + consts_offset, code.ctab_count * sizeof(*code.constants));
     }
 
-    code.ptr = fxlc.data.code;
-    code.end = (uint32_t *)((uint8_t *)fxlc.data.code + fxlc.data.size);
+    code.code = fxlc.data.code;
+    code.size = fxlc.data.size / sizeof(uint32_t);
 
     fx_parse_fxlvm_expression(parser, &code);
 }
diff --git a/libs/vkd3d/libs/vkd3d-shader/glsl.c b/libs/vkd3d/libs/vkd3d-shader/glsl.c
index 96c64a0e4c40..f250ecaa6467 100644
--- a/libs/vkd3d/libs/vkd3d-shader/glsl.c
+++ b/libs/vkd3d/libs/vkd3d-shader/glsl.c
@@ -70,16 +70,17 @@ struct vkd3d_glsl_generator
 static void shader_glsl_print_subscript(struct vkd3d_string_buffer *buffer,
         struct vkd3d_glsl_generator *gen, const struct vsir_src_operand *rel_addr, unsigned int offset);
 
-static void VKD3D_PRINTF_FUNC(3, 4) vkd3d_glsl_compiler_error(
-        struct vkd3d_glsl_generator *generator,
-        enum vkd3d_shader_error error, const char *fmt, ...)
+#define vkd3d_glsl_compiler_error(gen, error, ...) \
+        vkd3d_glsl_compiler_error_(gen, error, __FUNCTION__, __VA_ARGS__)
+static void VKD3D_PRINTF_FUNC(4, 5) vkd3d_glsl_compiler_error_(struct vkd3d_glsl_generator *gen,
+        enum vkd3d_shader_error error, const char *function, const char *fmt, ...)
 {
     va_list args;
 
     va_start(args, fmt);
-    vkd3d_shader_verror(generator->message_context, &generator->location, error, fmt, args);
+    vkd3d_shader_verror(gen->message_context, &gen->location, error, function, fmt, args);
     va_end(args);
-    generator->failed = true;
+    gen->failed = true;
 }
 
 static const char *shader_glsl_get_prefix(enum vkd3d_shader_type type)
@@ -187,7 +188,7 @@ static void shader_glsl_print_image_name(struct vkd3d_string_buffer *buffer,
 }
 
 static void shader_glsl_print_register_name(struct vkd3d_string_buffer *buffer,
-        struct vkd3d_glsl_generator *gen, const struct vkd3d_shader_register *reg)
+        struct vkd3d_glsl_generator *gen, const struct vsir_operand *reg)
 {
     switch (reg->type)
     {
@@ -391,7 +392,7 @@ static void shader_glsl_print_bitcast(struct vkd3d_string_buffer *dst, struct vk
 static void shader_glsl_print_src(struct vkd3d_string_buffer *buffer, struct vkd3d_glsl_generator *gen,
         const struct vsir_src_operand *vsir_src, uint32_t mask, enum vsir_data_type data_type)
 {
-    const struct vkd3d_shader_register *reg = &vsir_src->reg;
+    const struct vsir_operand *reg = &vsir_src->reg;
     struct vkd3d_string_buffer *register_name;
     enum vsir_data_type src_data_type;
     unsigned int size;
@@ -2322,15 +2323,32 @@ static void shader_glsl_generate_output_declarations(struct vkd3d_glsl_generator
 static void shader_glsl_handle_global_flags(struct vkd3d_string_buffer *buffer,
         struct vkd3d_glsl_generator *gen, enum vsir_global_flags flags)
 {
+    static const uint64_t ignored_flags = VKD3DSGF_REFACTORING_ALLOWED | VKD3DSGF_BIND_FOR_DURATION;
+
     if (flags & VKD3DSGF_FORCE_EARLY_DEPTH_STENCIL)
     {
         vkd3d_string_buffer_printf(buffer, "layout(early_fragment_tests) in;\n");
         flags &= ~VKD3DSGF_FORCE_EARLY_DEPTH_STENCIL;
     }
 
-    if (flags & ~VKD3DSGF_REFACTORING_ALLOWED)
+    if (flags & ignored_flags)
+    {
+        TRACE("Ignoring global flags %#"PRIx64".\n", flags & ignored_flags);
+        flags &= ~ignored_flags;
+    }
+
+    if (flags)
         vkd3d_glsl_compiler_error(gen, VKD3D_SHADER_ERROR_GLSL_INTERNAL,
                 "Internal compiler error: Unhandled global flags %#"PRIx64".", (uint64_t)flags);
+
+    if (gen->program->f16_denormal_mode != VKD3D_SHADER_DENORMAL_MODE_ANY
+            || gen->program->f32_denormal_mode != VKD3D_SHADER_DENORMAL_MODE_ANY
+            || gen->program->f64_denormal_mode != VKD3D_SHADER_DENORMAL_MODE_ANY)
+    {
+        vkd3d_glsl_compiler_error(gen, VKD3D_SHADER_ERROR_GLSL_UNSUPPORTED,
+                "Cannot emit denormal modes. The target environment does not support float controls.");
+        return;
+    }
 }
 
 static void shader_glsl_generate_declarations(struct vkd3d_glsl_generator *gen)
diff --git a/libs/vkd3d/libs/vkd3d-shader/hlsl.c b/libs/vkd3d/libs/vkd3d-shader/hlsl.c
index c5c723be9130..968f1a5ca021 100644
--- a/libs/vkd3d/libs/vkd3d-shader/hlsl.c
+++ b/libs/vkd3d/libs/vkd3d-shader/hlsl.c
@@ -22,40 +22,41 @@
 #include "hlsl.h"
 #include <stdio.h>
 
-void hlsl_note(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
-        enum vkd3d_shader_log_level level, const char *fmt, ...)
+void hlsl_note_(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
+        enum vkd3d_shader_log_level level, const char *function, const char *fmt, ...)
 {
     va_list args;
 
     va_start(args, fmt);
-    vkd3d_shader_vnote(ctx->message_context, loc, level, fmt, args);
+    vkd3d_shader_vnote(ctx->message_context, loc, level, function, fmt, args);
     va_end(args);
 }
 
-void hlsl_error(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
-        enum vkd3d_shader_error error, const char *fmt, ...)
+void hlsl_error_(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
+        enum vkd3d_shader_error error, const char *function, const char *fmt, ...)
 {
     va_list args;
 
     va_start(args, fmt);
-    vkd3d_shader_verror(ctx->message_context, loc, error, fmt, args);
+    vkd3d_shader_verror(ctx->message_context, loc, error, function, fmt, args);
     va_end(args);
 
     if (!ctx->result)
         ctx->result = VKD3D_ERROR_INVALID_SHADER;
 }
 
-void hlsl_warning(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
-        enum vkd3d_shader_error error, const char *fmt, ...)
+void hlsl_warning_(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
+        enum vkd3d_shader_error error, const char *function, const char *fmt, ...)
 {
     va_list args;
 
     va_start(args, fmt);
-    vkd3d_shader_vwarning(ctx->message_context, loc, error, fmt, args);
+    vkd3d_shader_vwarning(ctx->message_context, loc, error, function, fmt, args);
     va_end(args);
 }
 
-void hlsl_fixme(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc, const char *fmt, ...)
+void hlsl_fixme_(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
+        const char *function, const char *fmt, ...)
 {
     struct vkd3d_string_buffer *string;
     va_list args;
@@ -64,7 +65,8 @@ void hlsl_fixme(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc, c
     string = hlsl_get_string_buffer(ctx);
     vkd3d_string_buffer_printf(string, "Aborting due to not yet implemented feature: ");
     vkd3d_string_buffer_vprintf(string, fmt, args);
-    vkd3d_shader_error(ctx->message_context, loc, VKD3D_SHADER_ERROR_HLSL_NOT_IMPLEMENTED, "%s", string->buffer);
+    vkd3d_shader_error_(ctx->message_context, loc,
+            VKD3D_SHADER_ERROR_HLSL_NOT_IMPLEMENTED, function, "%s", string->buffer);
     hlsl_release_string_buffer(ctx, string);
     va_end(args);
 
@@ -96,18 +98,39 @@ char *hlsl_sprintf_alloc(struct hlsl_ctx *ctx, const char *fmt, ...)
 void hlsl_add_var(struct hlsl_ctx *ctx, struct hlsl_ir_var *decl)
 {
     struct hlsl_scope *scope = ctx->cur_scope;
+    bool allow_redefinition = false;
     struct hlsl_ir_var *var;
 
+    /* If a variable is declared in a loop's initialization, it is considered
+     * to be declared outside the loop, and redefinition is allowed. */
+    if (scope->loop)
+    {
+        allow_redefinition = true;
+        scope = scope->upper;
+        VKD3D_ASSERT(scope);
+    }
+
     if (decl->name)
     {
         LIST_FOR_EACH_ENTRY(var, &scope->vars, struct hlsl_ir_var, scope_entry)
         {
             if (var->name && !strcmp(decl->name, var->name))
             {
-                hlsl_error(ctx, &decl->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
-                        "Identifier \"%s\" was already declared in this scope.", var->name);
-                hlsl_note(ctx, &var->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously declared here.", var->name);
-                break;
+                if (allow_redefinition)
+                {
+                    hlsl_warning(ctx, &decl->loc, VKD3D_SHADER_WARNING_HLSL_REDEFINED,
+                            "Identifier \"%s\" was already declared in this scope.", var->name);
+                    hlsl_note(ctx, &var->loc, VKD3D_SHADER_LOG_ERROR,
+                            "\"%s\" was previously declared here.", var->name);
+                }
+                else
+                {
+                    hlsl_error(ctx, &decl->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
+                            "Identifier \"%s\" was already declared in this scope.", var->name);
+                    hlsl_note(ctx, &var->loc, VKD3D_SHADER_LOG_ERROR,
+                            "\"%s\" was previously declared here.", var->name);
+                    break;
+                }
             }
         }
     }
@@ -119,7 +142,7 @@ struct hlsl_ir_var *hlsl_get_var(struct hlsl_scope *scope, const char *name)
 {
     struct hlsl_ir_var *var;
 
-    LIST_FOR_EACH_ENTRY(var, &scope->vars, struct hlsl_ir_var, scope_entry)
+    LIST_FOR_EACH_ENTRY_REV(var, &scope->vars, struct hlsl_ir_var, scope_entry)
     {
         if (var->name && !strcmp(name, var->name))
             return var;
@@ -955,7 +978,7 @@ struct hlsl_type *hlsl_deref_get_type(struct hlsl_ctx *ctx, const struct hlsl_de
 
 /* Initializes a deref from another deref (prefix) and a component index.
  * *block is initialized to contain the new constant node instructions used by the deref's path. */
-static bool init_deref_from_component_index(struct hlsl_ctx *ctx, struct hlsl_block *block,
+bool hlsl_init_deref_from_component_index(struct hlsl_ctx *ctx, struct hlsl_block *block,
         struct hlsl_deref *deref, const struct hlsl_deref *prefix, unsigned int index,
         const struct vkd3d_shader_location *loc)
 {
@@ -1023,6 +1046,10 @@ struct hlsl_type *hlsl_get_element_type_from_path_index(struct hlsl_ctx *ctx, co
             return type->e.record.fields[c->value.u[0].u].type;
         }
 
+        case HLSL_CLASS_TEXTURE:
+        case HLSL_CLASS_UAV:
+            return type->e.resource.format;
+
         default:
             vkd3d_unreachable();
     }
@@ -1431,11 +1458,7 @@ struct hlsl_type *hlsl_type_clone(struct hlsl_ctx *ctx, struct hlsl_type *old,
                     return NULL;
                 }
                 dst_field->name = hlsl_strdup(ctx, src_field->name);
-                if (src_field->semantic.name)
-                {
-                    dst_field->semantic.name = hlsl_strdup(ctx, src_field->semantic.name);
-                    dst_field->semantic.index = src_field->semantic.index;
-                }
+                hlsl_clone_semantic(ctx, &dst_field->semantic, &src_field->semantic);
             }
             break;
         }
@@ -1726,7 +1749,7 @@ void hlsl_block_add_store_component(struct hlsl_ctx *ctx, struct hlsl_block *blo
         return;
     init_node(&store->node, HLSL_IR_STORE, NULL, &rhs->loc);
 
-    if (!init_deref_from_component_index(ctx, &comp_path_block, &store->lhs, lhs, comp, &rhs->loc))
+    if (!hlsl_init_deref_from_component_index(ctx, &comp_path_block, &store->lhs, lhs, comp, &rhs->loc))
     {
         vkd3d_free(store);
         return;
@@ -2009,7 +2032,8 @@ static struct hlsl_ir_node *hlsl_new_error_expr(struct hlsl_ctx *ctx)
 }
 
 struct hlsl_ir_node *hlsl_new_if(struct hlsl_ctx *ctx, struct hlsl_ir_node *condition, struct hlsl_block *then_block,
-        struct hlsl_block *else_block, enum hlsl_if_flatten_type flatten_type, const struct vkd3d_shader_location *loc)
+        struct hlsl_block *else_block, enum hlsl_if_flatten_type flatten_type, bool is_loop_condition,
+        const struct vkd3d_shader_location *loc)
 {
     struct hlsl_ir_if *iff;
 
@@ -2023,14 +2047,16 @@ struct hlsl_ir_node *hlsl_new_if(struct hlsl_ctx *ctx, struct hlsl_ir_node *cond
     hlsl_block_init(&iff->else_block);
     if (else_block)
         hlsl_block_add_block(&iff->else_block, else_block);
+    iff->is_loop_conditional = is_loop_condition;
     return &iff->node;
 }
 
 void hlsl_block_add_if(struct hlsl_ctx *ctx, struct hlsl_block *block,
         struct hlsl_ir_node *condition, struct hlsl_block *then_block, struct hlsl_block *else_block,
-        enum hlsl_if_flatten_type flatten_type, const struct vkd3d_shader_location *loc)
+        enum hlsl_if_flatten_type flatten_type, bool is_loop_condition, const struct vkd3d_shader_location *loc)
 {
-    struct hlsl_ir_node *instr = hlsl_new_if(ctx, condition, then_block, else_block, flatten_type, loc);
+    struct hlsl_ir_node *instr = hlsl_new_if(ctx, condition, then_block,
+            else_block, flatten_type, is_loop_condition, loc);
 
     if (instr)
     {
@@ -2164,7 +2190,7 @@ struct hlsl_ir_node *hlsl_block_add_load_component(struct hlsl_ctx *ctx, struct
     comp_type = hlsl_type_get_component_type(ctx, type, comp);
     init_node(&load->node, HLSL_IR_LOAD, comp_type, loc);
 
-    if (!init_deref_from_component_index(ctx, &comp_path_block, &load->src, deref, comp, loc))
+    if (!hlsl_init_deref_from_component_index(ctx, &comp_path_block, &load->src, deref, comp, loc))
     {
         vkd3d_free(load);
         block->value = ctx->error_instr;
@@ -2239,8 +2265,8 @@ static struct hlsl_ir_node *hlsl_new_resource_store(struct hlsl_ctx *ctx, enum h
 {
     struct hlsl_ir_resource_store *store;
 
-    if (type != HLSL_RESOURCE_STORE
-            || hlsl_deref_get_type(ctx, resource)->sampler_dim != HLSL_SAMPLER_DIM_STRUCTURED_BUFFER)
+    if (type != HLSL_RESOURCE_STORE || (!resource->var->is_tgsm
+            && hlsl_deref_get_type(ctx, resource)->sampler_dim != HLSL_SAMPLER_DIM_STRUCTURED_BUFFER))
         VKD3D_ASSERT(!byte_offset);
 
     if (!(store = hlsl_alloc(ctx, sizeof(*store))))
@@ -2363,6 +2389,18 @@ struct hlsl_ir_node *hlsl_new_compile(struct hlsl_ctx *ctx, const struct hlsl_pr
             type = hlsl_get_type(ctx->cur_scope, "GeometryShader", true, true);
             break;
 
+        case VKD3D_SHADER_TYPE_HULL:
+            type = hlsl_get_type(ctx->cur_scope, "HullShader", true, true);
+            break;
+
+        case VKD3D_SHADER_TYPE_DOMAIN:
+            type = hlsl_get_type(ctx->cur_scope, "DomainShader", true, true);
+            break;
+
+        case VKD3D_SHADER_TYPE_COMPUTE:
+            type = hlsl_get_type(ctx->cur_scope, "ComputeShader", true, true);
+            break;
+
         default:
             hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_PROFILE, "Invalid profile \"%s\".", profile->name);
             return NULL;
@@ -2751,8 +2789,8 @@ static struct hlsl_ir_node *clone_if(struct hlsl_ctx *ctx, struct clone_instr_ma
         return NULL;
     }
 
-    if (!(dst = hlsl_new_if(ctx, map_instr(map, src->condition.node),
-            &then_block, &else_block, src->flatten_type, &src->node.loc)))
+    if (!(dst = hlsl_new_if(ctx, map_instr(map, src->condition.node), &then_block,
+            &else_block, src->flatten_type, src->is_loop_conditional, &src->node.loc)))
     {
         hlsl_block_cleanup(&then_block);
         hlsl_block_cleanup(&else_block);
@@ -2804,6 +2842,9 @@ static struct hlsl_ir_node *clone_loop(struct hlsl_ctx *ctx, struct clone_instr_
         hlsl_block_cleanup(&body);
         return NULL;
     }
+
+    hlsl_ir_loop(dst)->limiter = src->limiter;
+    hlsl_ir_loop(dst)->limiter_component = src->limiter_component;
     return dst;
 }
 
@@ -3869,7 +3910,10 @@ static void dump_ir_if(struct hlsl_ctx *ctx, struct vkd3d_string_buffer *buffer,
 {
     vkd3d_string_buffer_printf(buffer, "if (");
     dump_src(buffer, &if_node->condition);
-    vkd3d_string_buffer_printf(buffer, ") {\n");
+    vkd3d_string_buffer_printf(buffer, ") {");
+    if (if_node->is_loop_conditional)
+        vkd3d_string_buffer_printf(buffer, " // loop conditional.");
+    vkd3d_string_buffer_printf(buffer, "\n");
     dump_block(ctx, buffer, &if_node->then_block);
     vkd3d_string_buffer_printf(buffer, "      %10s   } else {\n", "");
     dump_block(ctx, buffer, &if_node->else_block);
@@ -3915,7 +3959,10 @@ static void dump_ir_jump(struct vkd3d_string_buffer *buffer, const struct hlsl_i
 
 static void dump_ir_loop(struct hlsl_ctx *ctx, struct vkd3d_string_buffer *buffer, const struct hlsl_ir_loop *loop)
 {
-    vkd3d_string_buffer_printf(buffer, "for (;;) {\n");
+    vkd3d_string_buffer_printf(buffer, "for (;;) {");
+    if (loop->limiter)
+        vkd3d_string_buffer_printf(buffer, " // limiter: %s[%u]", loop->limiter->name, loop->limiter_component);
+    vkd3d_string_buffer_printf(buffer, "\n");
     dump_block(ctx, buffer, &loop->body);
     vkd3d_string_buffer_printf(buffer, "      %10s   }", "");
 }
@@ -4080,8 +4127,11 @@ static void dump_ir_interlocked(struct vkd3d_string_buffer *buffer, const struct
     VKD3D_ASSERT(interlocked->op < ARRAY_SIZE(op_names));
     vkd3d_string_buffer_printf(buffer, "interlocked_%s(dst = ", op_names[interlocked->op]);
     dump_deref(buffer, &interlocked->dst);
-    vkd3d_string_buffer_printf(buffer, ", coords = ");
-    dump_src(buffer, &interlocked->coords);
+    if (interlocked->coords.node)
+    {
+        vkd3d_string_buffer_printf(buffer, ", coords = ");
+        dump_src(buffer, &interlocked->coords);
+    }
     if (interlocked->cmp_value.node)
     {
         vkd3d_string_buffer_printf(buffer, ", cmp_value = ");
@@ -5049,6 +5099,43 @@ static void declare_predefined_types(struct hlsl_ctx *ctx)
     hlsl_release_string_buffer(ctx, name);
 }
 
+void hlsl_ctx_init_entry_function_attributes(struct hlsl_ctx *ctx)
+{
+    ctx->domain = VKD3D_TESSELLATOR_DOMAIN_INVALID;
+    ctx->output_control_point_count = UINT_MAX;
+    ctx->output_control_point_type = NULL;
+    ctx->output_primitive = 0;
+    ctx->partitioning = 0;
+    ctx->patch_constant_func = NULL;
+    ctx->input_control_point_count = UINT_MAX;
+    ctx->input_control_point_type = NULL;
+    ctx->input_primitive_param = NULL;
+    ctx->max_vertex_count = 0;
+    ctx->input_primitive_type = VKD3D_PT_UNDEFINED;
+    ctx->output_topology_type = VKD3D_PT_UNDEFINED;
+
+    ctx->found_numthreads = 0;
+    memset(ctx->thread_count, 0, sizeof(ctx->thread_count));
+}
+
+static void hlsl_ctx_cleanup_locale(struct hlsl_ctx *ctx)
+{
+#ifdef _WIN32
+    _free_locale(ctx->c_locale);
+#else
+    freelocale(ctx->c_locale);
+#endif
+}
+
+static void hlsl_ctx_init_locale(struct hlsl_ctx *ctx)
+{
+#ifdef _WIN32
+    ctx->c_locale = _create_locale(LC_ALL, "C");
+#else
+    ctx->c_locale = newlocale(LC_ALL_MASK, "C", NULL);
+#endif
+}
+
 static bool hlsl_ctx_init(struct hlsl_ctx *ctx, struct vkd3d_shader_source_list *source_files,
         const struct vkd3d_shader_compile_info *compile_info, const struct hlsl_profile_info *profile,
         struct vkd3d_shader_message_context *message_context)
@@ -5070,10 +5157,13 @@ static bool hlsl_ctx_init(struct hlsl_ctx *ctx, struct vkd3d_shader_source_list
     ctx->location.line = ctx->location.column = 1;
     vkd3d_string_buffer_cache_init(&ctx->string_buffers);
 
+    hlsl_ctx_init_locale(ctx);
+
     list_init(&ctx->scopes);
 
     if (!(ctx->dummy_scope = hlsl_new_scope(ctx, NULL)))
     {
+        hlsl_ctx_cleanup_locale(ctx);
         vkd3d_string_buffer_cache_cleanup(&ctx->string_buffers);
         return false;
     }
@@ -5092,10 +5182,16 @@ static bool hlsl_ctx_init(struct hlsl_ctx *ctx, struct vkd3d_shader_source_list
 
     if (!(ctx->globals_buffer = hlsl_new_buffer(ctx, HLSL_BUFFER_CONSTANT,
             hlsl_strdup(ctx, "$Globals"), 0, NULL, NULL, &ctx->location)))
+    {
+        hlsl_ctx_cleanup_locale(ctx);
         return false;
+    }
     if (!(ctx->params_buffer = hlsl_new_buffer(ctx, HLSL_BUFFER_CONSTANT,
             hlsl_strdup(ctx, "$Params"), 0, NULL, NULL, &ctx->location)))
+    {
+        hlsl_ctx_cleanup_locale(ctx);
         return false;
+    }
     ctx->cur_buffer = ctx->globals_buffer;
 
     ctx->warn_implicit_truncation = true;
@@ -5114,8 +5210,7 @@ static bool hlsl_ctx_init(struct hlsl_ctx *ctx, struct vkd3d_shader_source_list
                 break;
 
             case VKD3D_SHADER_COMPILE_OPTION_BACKWARD_COMPATIBILITY:
-                ctx->semantic_compat_mapping = option->value & VKD3D_SHADER_COMPILE_OPTION_BACKCOMPAT_MAP_SEMANTIC_NAMES;
-                ctx->double_as_float_alias = option->value & VKD3D_SHADER_COMPILE_OPTION_DOUBLE_AS_FLOAT_ALIAS;
+                ctx->compatibility_flags = option->value;
                 break;
 
             case VKD3D_SHADER_COMPILE_OPTION_CHILD_EFFECT:
@@ -5136,17 +5231,13 @@ static bool hlsl_ctx_init(struct hlsl_ctx *ctx, struct vkd3d_shader_source_list
     }
 
     if (!(ctx->error_instr = hlsl_new_error_expr(ctx)))
+    {
+        hlsl_ctx_cleanup_locale(ctx);
         return false;
+    }
     hlsl_block_add_instr(&ctx->static_initializers, ctx->error_instr);
 
-    ctx->domain = VKD3D_TESSELLATOR_DOMAIN_INVALID;
-    ctx->output_control_point_count = UINT_MAX;
-    ctx->output_primitive = 0;
-    ctx->partitioning = 0;
-    ctx->input_control_point_count = UINT_MAX;
-    ctx->max_vertex_count = 0;
-    ctx->input_primitive_type = VKD3D_PT_UNDEFINED;
-    ctx->output_topology_type = VKD3D_PT_UNDEFINED;
+    hlsl_ctx_init_entry_function_attributes(ctx);
 
     return true;
 }
@@ -5198,6 +5289,7 @@ static void hlsl_ctx_cleanup(struct hlsl_ctx *ctx)
     }
 
     vkd3d_free(ctx->constant_defs.regs);
+    hlsl_ctx_cleanup_locale(ctx);
 }
 
 static int hlsl_ctx_parse(struct hlsl_ctx *ctx, struct vkd3d_shader_source_list *source_list,
@@ -5334,7 +5426,7 @@ int hlsl_parse(const struct vkd3d_shader_compile_info *compile_info,
     if (!vsir_program_init(program, compile_info, &version, 0, VSIR_CF_STRUCTURED, normalisation_level))
         return VKD3D_ERROR_OUT_OF_MEMORY;
 
-    program->f32_denorm_mode = VSIR_DENORM_FLUSH_TO_ZERO;
+    program->f32_denormal_mode = VKD3D_SHADER_DENORMAL_MODE_FLUSH_TO_ZERO;
 
     if ((ret = hlsl_ctx_parse(&ctx, &program->source_files, compile_info, profile, message_context)) < 0)
     {
diff --git a/libs/vkd3d/libs/vkd3d-shader/hlsl.h b/libs/vkd3d/libs/vkd3d-shader/hlsl.h
index 871479851294..d996f635648c 100644
--- a/libs/vkd3d/libs/vkd3d-shader/hlsl.h
+++ b/libs/vkd3d/libs/vkd3d-shader/hlsl.h
@@ -554,6 +554,10 @@ struct hlsl_ir_var
      * sampler and a texture for SM<4 backwards compatibility. */
     bool is_combined_sampler;
 
+    /* Whether the initial value of the variable is a compile-time constant
+     * expression and therefore it could be moved to ctx->static_initializers. */
+    bool is_compile_time_const;
+
     uint32_t is_input_semantic : 1;
     uint32_t is_output_semantic : 1;
     uint32_t is_uniform : 1;
@@ -683,6 +687,9 @@ struct hlsl_ir_if
     struct hlsl_block then_block;
     struct hlsl_block else_block;
     enum hlsl_if_flatten_type flatten_type;
+    /* If this "if" was created inside a "for" loop to take care of breaking it
+     * when the conditional is false. */
+    bool is_loop_conditional;
 };
 
 enum hlsl_loop_unroll_type
@@ -709,6 +716,11 @@ struct hlsl_ir_loop
     unsigned int next_index; /* liveness index of the end of the loop */
     struct hlsl_src unroll_limit;
     enum hlsl_loop_unroll_type unroll_type;
+
+    /* Uniform variable that contains an int that defines a limit for the
+     * number of iterations, if any. */
+    struct hlsl_ir_var *limiter;
+    unsigned int limiter_component;
 };
 
 struct hlsl_ir_switch_case
@@ -1071,11 +1083,6 @@ struct hlsl_profile_info
     bool software;
 };
 
-struct hlsl_vec4
-{
-    float f[4];
-};
-
 enum hlsl_buffer_type
 {
     HLSL_BUFFER_CONSTANT,
@@ -1134,6 +1141,8 @@ struct hlsl_ctx
      *   scanner is declared as reentrant, which is the case. */
     void *scanner;
 
+    vkd3d_locale c_locale;
+
     /* Pointer to the current scope; changes as the parser reads the code. */
     struct hlsl_scope *cur_scope;
     /* Scope of global variables. */
@@ -1191,21 +1200,25 @@ struct hlsl_ctx
     /* List of the instruction nodes for initializing static variables. */
     struct hlsl_block static_initializers;
 
-    /* Dynamic array of constant values that appear in the shader, associated to the 'c' registers.
-     * Only used for SM1 profiles. */
+    /* Dynamic array of constant values that appear in the shader, associated
+     * to the 'c' and 'i' registers. Only used for SM1 profiles. */
     struct hlsl_constant_defs
     {
         struct hlsl_constant_register
         {
+            bool is_int;
             uint32_t index;
             uint32_t allocated_mask;
-            struct hlsl_vec4 value;
+            union hlsl_constant_value_component value[4];
             struct vkd3d_shader_location loc;
         } *regs;
         size_t count, size;
     } constant_defs;
     /* 'c' registers where the constants expected by SM2 sincos are stored. */
     struct hlsl_reg d3dsincosconst1, d3dsincosconst2;
+    /* 'i' register allocated by SM3 to keep the maximum number of iterations. */
+    struct hlsl_reg d3d255intconst;
+
     /* Number of allocated registers, used in translation to vsir. */
     unsigned int ssa_count, temp_count, indexable_temp_count;
 
@@ -1263,11 +1276,10 @@ struct hlsl_ctx
     /* Whether the numthreads() attribute has been provided in the entry-point function. */
     uint32_t found_numthreads : 1;
 
-    bool semantic_compat_mapping;
+    enum vkd3d_shader_compile_option_backward_compatibility compatibility_flags;
     bool child_effect;
     bool include_empty_buffers;
     bool warn_implicit_truncation;
-    bool double_as_float_alias;
 };
 
 static inline bool hlsl_version_ge(const struct hlsl_ctx *ctx, unsigned int major, unsigned int minor)
@@ -1528,6 +1540,13 @@ static inline bool hlsl_is_numeric_type(const struct hlsl_type *type)
     return type->class <= HLSL_CLASS_LAST_NUMERIC;
 }
 
+static inline struct hlsl_type *hlsl_change_base_type(const struct hlsl_ctx *ctx,
+        const struct hlsl_type *type, enum hlsl_base_type base_type)
+{
+    VKD3D_ASSERT(hlsl_is_numeric_type(type));
+    return hlsl_get_numeric_type(ctx, type->class, base_type, type->e.numeric.dimx, type->e.numeric.dimy);
+}
+
 static inline bool hlsl_is_vec1(const struct hlsl_type *type)
 {
     return type->class == HLSL_CLASS_SCALAR || (type->class == HLSL_CLASS_VECTOR && type->e.numeric.dimx == 1);
@@ -1600,7 +1619,7 @@ struct hlsl_ir_node *hlsl_block_add_float_constant(struct hlsl_ctx *ctx, struct
         float f, const struct vkd3d_shader_location *loc);
 void hlsl_block_add_if(struct hlsl_ctx *ctx, struct hlsl_block *block,
         struct hlsl_ir_node *condition, struct hlsl_block *then_block, struct hlsl_block *else_block,
-        enum hlsl_if_flatten_type flatten_type, const struct vkd3d_shader_location *loc);
+        enum hlsl_if_flatten_type flatten_type, bool is_loop_condition, const struct vkd3d_shader_location *loc);
 struct hlsl_ir_node *hlsl_block_add_index(struct hlsl_ctx *ctx, struct hlsl_block *block,
         struct hlsl_ir_node *val, struct hlsl_ir_node *idx, const struct vkd3d_shader_location *loc);
 struct hlsl_ir_node *hlsl_block_add_int_constant(struct hlsl_ctx *ctx, struct hlsl_block *block,
@@ -1666,6 +1685,8 @@ int hlsl_emit_vsir(struct hlsl_ctx *ctx, const struct vkd3d_shader_compile_info
         struct vsir_program *program, struct vkd3d_shader_code *reflection_data);
 
 bool hlsl_init_deref(struct hlsl_ctx *ctx, struct hlsl_deref *deref, struct hlsl_ir_var *var, unsigned int path_len);
+bool hlsl_init_deref_from_component_index(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_deref *deref,
+        const struct hlsl_deref *prefix, unsigned int index, const struct vkd3d_shader_location *loc);
 bool hlsl_init_deref_from_index_chain(struct hlsl_ctx *ctx, struct hlsl_deref *deref, struct hlsl_ir_node *chain);
 bool hlsl_copy_deref(struct hlsl_ctx *ctx, struct hlsl_deref *deref, const struct hlsl_deref *other);
 
@@ -1677,6 +1698,8 @@ bool hlsl_clone_semantic(struct hlsl_ctx *ctx, struct hlsl_semantic *dst, const
 void hlsl_cleanup_ir_switch_cases(struct list *cases);
 void hlsl_free_ir_switch_case(struct hlsl_ir_switch_case *c);
 
+void hlsl_ctx_init_entry_function_attributes(struct hlsl_ctx *ctx);
+
 void hlsl_replace_node(struct hlsl_ir_node *old, struct hlsl_ir_node *new);
 
 void hlsl_free_attribute(struct hlsl_attribute *attr);
@@ -1721,7 +1744,8 @@ struct hlsl_ir_function_decl *hlsl_new_func_decl(struct hlsl_ctx *ctx, uint32_t
         struct hlsl_type *return_type, const struct hlsl_func_parameters *parameters,
         const struct hlsl_semantic *semantic, const struct vkd3d_shader_location *loc);
 struct hlsl_ir_node *hlsl_new_if(struct hlsl_ctx *ctx, struct hlsl_ir_node *condition, struct hlsl_block *then_block,
-        struct hlsl_block *else_block, enum hlsl_if_flatten_type flatten_type, const struct vkd3d_shader_location *loc);
+        struct hlsl_block *else_block, enum hlsl_if_flatten_type flatten_type, bool is_loop_condition,
+        const struct vkd3d_shader_location *loc);
 struct hlsl_type *hlsl_new_stream_output_type(struct hlsl_ctx *ctx,
         enum hlsl_so_object_type so_type, struct hlsl_type *type);
 struct hlsl_ir_node *hlsl_new_ternary_expr(struct hlsl_ctx *ctx, enum hlsl_ir_expr_op op,
@@ -1789,14 +1813,22 @@ struct hlsl_ir_switch_case *hlsl_new_switch_case(struct hlsl_ctx *ctx, unsigned
 struct hlsl_ir_node *hlsl_new_switch(struct hlsl_ctx *ctx, struct hlsl_ir_node *selector,
         struct list *cases, const struct vkd3d_shader_location *loc);
 
-void hlsl_error(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
-        enum vkd3d_shader_error error, const char *fmt, ...) VKD3D_PRINTF_FUNC(4, 5);
-void hlsl_fixme(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
-        const char *fmt, ...) VKD3D_PRINTF_FUNC(3, 4);
-void hlsl_warning(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
-        enum vkd3d_shader_error error, const char *fmt, ...) VKD3D_PRINTF_FUNC(4, 5);
-void hlsl_note(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
-        enum vkd3d_shader_log_level level, const char *fmt, ...) VKD3D_PRINTF_FUNC(4, 5);
+#define hlsl_error(ctx, loc, error, ...) \
+        hlsl_error_(ctx, loc, error, __FUNCTION__, __VA_ARGS__)
+void hlsl_error_(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
+        enum vkd3d_shader_error error, const char *function, const char *fmt, ...) VKD3D_PRINTF_FUNC(5, 6);
+#define hlsl_fixme(ctx, loc, ...) \
+        hlsl_fixme_(ctx, loc,  __FUNCTION__, __VA_ARGS__)
+void hlsl_fixme_(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
+        const char *function, const char *fmt, ...) VKD3D_PRINTF_FUNC(4, 5);
+#define hlsl_warning(ctx, loc, error, ...) \
+        hlsl_warning_(ctx, loc, error, __FUNCTION__, __VA_ARGS__)
+void hlsl_warning_(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
+        enum vkd3d_shader_error error, const char *function, const char *fmt, ...) VKD3D_PRINTF_FUNC(5, 6);
+#define hlsl_note(ctx, loc, level, ...) \
+        hlsl_note_(ctx, loc, level, __FUNCTION__, __VA_ARGS__)
+void hlsl_note_(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc,
+        enum vkd3d_shader_log_level level, const char *function, const char *fmt, ...) VKD3D_PRINTF_FUNC(5, 6);
 
 void hlsl_push_scope(struct hlsl_ctx *ctx);
 void hlsl_pop_scope(struct hlsl_ctx *ctx);
diff --git a/libs/vkd3d/libs/vkd3d-shader/hlsl.l b/libs/vkd3d/libs/vkd3d-shader/hlsl.l
index 24d08b314a49..85f0d89aa86d 100644
--- a/libs/vkd3d/libs/vkd3d-shader/hlsl.l
+++ b/libs/vkd3d/libs/vkd3d-shader/hlsl.l
@@ -19,6 +19,10 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+%top{
+#include "config.h"
+}
+
 %{
 #define YY_NO_UNISTD_H
 #include "hlsl.h"
@@ -238,15 +242,18 @@ while                   {return KW_WHILE;               }
                         }
 
 [0-9]*\.[0-9]+([eE][+-]?[0-9]+)?[h|H|f|F]? {
-                            yylval->floatval = atof(yytext);
+                            struct hlsl_ctx *ctx = yyget_extra(yyscanner);
+                            yylval->floatval = vkd3d_parse_float(yytext, ctx->c_locale);
                             return C_FLOAT;
                         }
 [0-9]+\.([eE][+-]?[0-9]+)?[h|H|f|F]? {
-                            yylval->floatval = atof(yytext);
+                            struct hlsl_ctx *ctx = yyget_extra(yyscanner);
+                            yylval->floatval = vkd3d_parse_float(yytext, ctx->c_locale);
                             return C_FLOAT;
                         }
 [0-9]+[eE][+-]?[0-9]+[h|H|f|F]? {
-                            yylval->floatval = atof(yytext);
+                            struct hlsl_ctx *ctx = yyget_extra(yyscanner);
+                            yylval->floatval = vkd3d_parse_float(yytext, ctx->c_locale);
                             return C_FLOAT;
                         }
 0x[0-9a-fA-F]+[lL]?     {
diff --git a/libs/vkd3d/libs/vkd3d-shader/hlsl.y b/libs/vkd3d/libs/vkd3d-shader/hlsl.y
index e74b9a6c4de8..6c1f73df70e0 100644
--- a/libs/vkd3d/libs/vkd3d-shader/hlsl.y
+++ b/libs/vkd3d/libs/vkd3d-shader/hlsl.y
@@ -471,7 +471,7 @@ static void append_conditional_break(struct hlsl_ctx *ctx, struct hlsl_block *co
 
     hlsl_block_init(&then_block);
     hlsl_block_add_jump(ctx, &then_block, HLSL_IR_JUMP_BREAK, NULL, &condition->loc);
-    hlsl_block_add_if(ctx, cond_block, not, &then_block, NULL, HLSL_IF_FLATTEN_DEFAULT, &condition->loc);
+    hlsl_block_add_if(ctx, cond_block, not, &then_block, NULL, HLSL_IF_FLATTEN_DEFAULT, true, &condition->loc);
 }
 
 static void check_attribute_list_for_duplicates(struct hlsl_ctx *ctx, const struct parse_attribute_list *attrs)
@@ -544,9 +544,10 @@ static void check_loop_attributes(struct hlsl_ctx *ctx, const struct parse_attri
         hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Unroll attribute can't be used with 'fastopt' attribute.");
 }
 
-static bool is_static_expression(const struct hlsl_block *block)
+static bool has_side_effects(const struct hlsl_block *block)
 {
     struct hlsl_ir_node *node;
+    struct hlsl_ir_var *var;
 
     LIST_FOR_EACH_ENTRY(node, &block->instrs, struct hlsl_ir_node, entry)
     {
@@ -558,13 +559,58 @@ static bool is_static_expression(const struct hlsl_block *block)
             case HLSL_IR_SAMPLER_STATE:
             case HLSL_IR_STRING_CONSTANT:
             case HLSL_IR_SWIZZLE:
+            case HLSL_IR_INDEX:
             case HLSL_IR_LOAD:
+                break;
+            case HLSL_IR_STORE:
+                var = hlsl_ir_store(node)->lhs.var;
+                if (var->is_synthetic)
+                    break;
+                return true;
+            case HLSL_IR_CALL:
+            case HLSL_IR_IF:
+            case HLSL_IR_INTERLOCKED:
+            case HLSL_IR_LOOP:
+            case HLSL_IR_JUMP:
+            case HLSL_IR_RESOURCE_LOAD:
+            case HLSL_IR_RESOURCE_STORE:
+            case HLSL_IR_SWITCH:
+            case HLSL_IR_STATEBLOCK_CONSTANT:
+            case HLSL_IR_SYNC:
+                return true;
+        }
+    }
+
+    return false;
+}
+
+static bool is_compile_time_const(const struct hlsl_block *block, bool default_values)
+{
+    struct hlsl_ir_node *node;
+    struct hlsl_ir_var *var;
+
+    LIST_FOR_EACH_ENTRY(node, &block->instrs, struct hlsl_ir_node, entry)
+    {
+        switch (node->type)
+        {
+            case HLSL_IR_COMPILE:
+            case HLSL_IR_CONSTANT:
+            case HLSL_IR_EXPR:
+            case HLSL_IR_SAMPLER_STATE:
+            case HLSL_IR_STRING_CONSTANT:
+            case HLSL_IR_SWIZZLE:
             case HLSL_IR_INDEX:
-                continue;
+                break;
+            case HLSL_IR_LOAD:
+                var = hlsl_ir_load(node)->src.var;
+                if (var->is_synthetic || var->is_compile_time_const || (var->default_values && default_values))
+                    break;
+                return false;
             case HLSL_IR_STORE:
-                if (hlsl_ir_store(node)->lhs.var->is_synthetic)
+                var = hlsl_ir_store(node)->lhs.var;
+                if (var->is_synthetic)
                     break;
-                /* fall-through */
+                return false;
             case HLSL_IR_CALL:
             case HLSL_IR_IF:
             case HLSL_IR_INTERLOCKED:
@@ -583,7 +629,8 @@ static bool is_static_expression(const struct hlsl_block *block)
 }
 
 static struct hlsl_default_value evaluate_static_expression(struct hlsl_ctx *ctx,
-        struct hlsl_block *block, struct hlsl_type *dst_type, const struct vkd3d_shader_location *loc)
+        struct hlsl_block *block, struct hlsl_type *dst_type, bool default_values,
+        const struct vkd3d_shader_location *loc)
 {
     struct hlsl_default_value ret = {0};
     struct hlsl_ir_node *node;
@@ -593,7 +640,7 @@ static struct hlsl_default_value evaluate_static_expression(struct hlsl_ctx *ctx
     if (node_from_block(block)->data_type->class == HLSL_CLASS_ERROR)
         return ret;
 
-    if (!is_static_expression(block))
+    if (!is_compile_time_const(block, default_values))
         hlsl_error(ctx, &node_from_block(block)->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX,
                 "Expected literal expression.");
 
@@ -644,7 +691,7 @@ static unsigned int evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, str
 {
     struct hlsl_default_value res;
 
-    res = evaluate_static_expression(ctx, block, hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT), loc);
+    res = evaluate_static_expression(ctx, block, hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT), false, loc);
     VKD3D_ASSERT(!res.string);
     return res.number.u;
 }
@@ -676,7 +723,7 @@ static struct hlsl_block *create_loop(struct hlsl_ctx *ctx, enum hlsl_loop_type
             {
                 struct hlsl_block expr;
 
-                if (!is_static_expression(&attr->instrs))
+                if (has_side_effects(&attr->instrs))
                 {
                     hlsl_error(ctx, &attr->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX,
                             "Unroll limit expressions cannot have side effects.");
@@ -1681,8 +1728,7 @@ static struct hlsl_ir_node *add_unary_logical_expr(struct hlsl_ctx *ctx, struct
     if (arg->data_type->class == HLSL_CLASS_ERROR)
         return arg;
 
-    bool_type = hlsl_get_numeric_type(ctx, arg->data_type->class, HLSL_TYPE_BOOL,
-            arg->data_type->e.numeric.dimx, arg->data_type->e.numeric.dimy);
+    bool_type = hlsl_change_base_type(ctx, arg->data_type, HLSL_TYPE_BOOL);
     args[0] = add_implicit_conversion(ctx, block, arg, bool_type, loc);
     return add_expr(ctx, block, op, args, bool_type, loc);
 }
@@ -2000,205 +2046,27 @@ static bool invert_swizzle_matrix(const struct hlsl_matrix_swizzle *swizzle,
     return true;
 }
 
-static bool add_resource_store(struct hlsl_ctx *ctx, struct hlsl_block *block,
-        struct hlsl_ir_index *lhs, struct hlsl_ir_index *resource_access,
-        struct hlsl_ir_node *rhs, unsigned int width, uint32_t writemask, bool matrix_writemask)
+static struct hlsl_ir_node *resolve_assignment_lhs(struct hlsl_ctx *ctx, struct hlsl_block *block,
+        bool is_function_out_arg, struct hlsl_ir_node *lhs, struct hlsl_type **lhs_type,
+        struct hlsl_ir_node **rhs, unsigned int *writemask, bool *matrix_writemask)
 {
-    struct hlsl_ir_node *coords = resource_access->idx.node;
-    struct hlsl_type *resource_type, *resource_format;
-    struct vkd3d_shader_location loc = lhs->node.loc;
-    unsigned int dim_count, expected_width;
-    struct hlsl_deref resource_deref;
-
-    if (!hlsl_init_deref_from_index_chain(ctx, &resource_deref, resource_access->val.node))
-        return false;
-
-    resource_type = hlsl_deref_get_type(ctx, &resource_deref);
-    resource_format = resource_type->e.resource.format;
-    expected_width = resource_format->e.numeric.dimx * resource_format->e.numeric.dimy;
-    VKD3D_ASSERT(resource_type->class == HLSL_CLASS_TEXTURE || resource_type->class == HLSL_CLASS_UAV);
-
-    if (resource_type->class != HLSL_CLASS_UAV)
-        hlsl_error(ctx, &loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Read-only resources cannot be stored to.");
-
-    dim_count = hlsl_sampler_dim_count(resource_type->sampler_dim);
-
-    VKD3D_ASSERT(coords->data_type->class == HLSL_CLASS_VECTOR);
-    VKD3D_ASSERT(coords->data_type->e.numeric.type == HLSL_TYPE_UINT);
-    VKD3D_ASSERT(coords->data_type->e.numeric.dimx == dim_count);
-
-    if (resource_type->sampler_dim == HLSL_SAMPLER_DIM_STRUCTURED_BUFFER)
-    {
-        struct hlsl_ir_node *field_offset = hlsl_block_add_uint_constant(ctx, block, 0, &loc);
-        struct hlsl_type *val_type = lhs->node.data_type;
-        struct hlsl_ir_index *ptr = lhs, *prev = NULL;
-
-        if (rhs->data_type->class > HLSL_CLASS_LAST_NUMERIC)
-        {
-            hlsl_fixme(ctx, &loc, "Composite type structured buffer stores.");
-            return false;
-        }
-
-        while (ptr != resource_access)
-        {
-            struct hlsl_ir_index *next = hlsl_ir_index(ptr->val.node);
-            struct hlsl_type *type = ptr->val.node->data_type;
-
-            if (hlsl_index_is_noncontiguous(next))
-            {
-                type = next->val.node->data_type;
-            }
-            else if (hlsl_index_is_noncontiguous(ptr))
-            {
-                if (prev)
-                {
-                    type = prev->val.node->data_type;
-                }
-                else
-                {
-                    val_type = ptr->val.node->data_type;
-                    type = ptr->node.data_type;
-                }
-            }
-
-            field_offset = hlsl_block_add_packed_index_offset_append(ctx,
-                    block, field_offset, ptr->idx.node, type, &loc);
-
-            prev = ptr;
-            ptr = next;
-        }
-
-        if (matrix_writemask || rhs->data_type->class == HLSL_CLASS_MATRIX)
-        {
-            /* Type of the minor inner vector. column type for row major,
-             * row type for column major. */
-            struct hlsl_type *minor_type = hlsl_get_vector_type(ctx,
-                    val_type->e.numeric.type, hlsl_type_minor_size(val_type));
-            struct hlsl_block writes;
-            bool written = false;
-
-            hlsl_block_init(&writes);
-
-            for (unsigned int i = 0, k = 0; i < val_type->e.numeric.dimy; ++i)
-            {
-                bool row_major = hlsl_type_is_row_major(val_type);
-                struct hlsl_ir_node *mtx_offset;
-                struct hlsl_ir_node *row_c;
-
-                row_c = hlsl_block_add_uint_constant(ctx, &writes, i, &loc);
-                mtx_offset = hlsl_block_add_packed_index_offset_append(ctx, &writes,
-                        field_offset, row_c, row_major ? val_type : minor_type, &loc);
-
-                for (unsigned int j = 0; j < val_type->e.numeric.dimx; ++j)
-                {
-                    struct hlsl_ir_node *column_c, *load, *col_offset;
-
-                    if (matrix_writemask)
-                    {
-                        unsigned int idx = i * 4 + j;
-
-                        if (!(writemask & (1u << idx)))
-                            continue;
-                    }
-
-                    written = true;
-                    column_c = hlsl_block_add_uint_constant(ctx, &writes, j, &loc);
-                    col_offset = hlsl_block_add_packed_index_offset_append(ctx, &writes,
-                            mtx_offset, column_c, row_major ? minor_type : val_type, &loc);
-                    load = hlsl_add_load_component(ctx, &writes, rhs, k++, &loc);
-                    hlsl_block_add_resource_store(ctx, &writes, HLSL_RESOURCE_STORE,
-                            &resource_deref, col_offset, coords, load, VKD3DSP_WRITEMASK_0, &loc);
-                }
-            }
-
-            if (written)
-                hlsl_block_add_block(block, &writes);
-            hlsl_block_cleanup(&writes);
-        }
-        else
-        {
-            unsigned int size = val_type->e.numeric.dimx;
-
-            for (unsigned int i = 0, k = 0; i < size; ++i)
-            {
-                struct hlsl_ir_node *c, *load, *offset;
-
-                if (!(writemask & (1u << i)))
-                    continue;
-
-                if (val_type->class == HLSL_CLASS_SCALAR)
-                {
-                    VKD3D_ASSERT(i == 0);
-                    VKD3D_ASSERT(size == 1);
-
-                    offset = field_offset;
-                }
-                else
-                {
-                    c = hlsl_block_add_uint_constant(ctx, block, i, &loc);
-                    offset = hlsl_block_add_packed_index_offset_append(ctx, block, field_offset, c, val_type, &loc);
-                }
-
-                load = hlsl_add_load_component(ctx, block, rhs, k++, &loc);
-                hlsl_block_add_resource_store(ctx, block, HLSL_RESOURCE_STORE,
-                        &resource_deref, offset, coords, load, VKD3DSP_WRITEMASK_0, &loc);
-            }
-        }
-    }
-    else
-    {
-        if (width != expected_width)
-            hlsl_error(ctx, &loc, VKD3D_SHADER_ERROR_HLSL_INVALID_WRITEMASK,
-                    "Resource store expressions must write to all components.");
-
-        hlsl_block_add_resource_store(ctx, block, HLSL_RESOURCE_STORE,
-                &resource_deref, NULL, coords, rhs, writemask, &loc);
-    }
-
-    hlsl_cleanup_deref(&resource_deref);
-
-    return true;
-}
-
-static bool add_assignment(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *lhs,
-        enum parse_assign_op assign_op, struct hlsl_ir_node *rhs, bool is_function_out_arg)
-{
-    struct hlsl_type *lhs_type = lhs->data_type;
-    unsigned int writemask = 0, width = 0;
-    struct hlsl_ir_index *resource_access;
-    bool matrix_writemask = false;
+    unsigned int width = 0;
     bool first_cast = true;
 
-    if (lhs->data_type->class == HLSL_CLASS_ERROR || rhs->data_type->class == HLSL_CLASS_ERROR)
-    {
-        block->value = ctx->error_instr;
-        return true;
-    }
-
-    if (assign_op == ASSIGN_OP_SUB)
-    {
-        if (!(rhs = add_unary_arithmetic_expr(ctx, block, HLSL_OP1_NEG, rhs, &rhs->loc)))
-            return false;
-        assign_op = ASSIGN_OP_ADD;
-    }
-    if (assign_op != ASSIGN_OP_ASSIGN)
+    if (hlsl_is_numeric_type(lhs->data_type))
     {
-        enum hlsl_ir_expr_op op = op_from_assignment(assign_op);
+        unsigned int size = hlsl_type_component_count(lhs->data_type);
 
-        VKD3D_ASSERT(op);
-        if (!(rhs = add_binary_expr(ctx, block, op, lhs, rhs, &rhs->loc)))
-            return false;
+        *writemask = (1 << size) - 1;
+        width = size;
     }
-
-    if (hlsl_is_numeric_type(lhs_type))
+    else
     {
-        unsigned int size = hlsl_type_component_count(lhs_type);
-
-        writemask = (1 << size) - 1;
-        width = size;
+        *writemask = 0;
     }
 
-    rhs = add_implicit_conversion(ctx, block, rhs, lhs_type, &rhs->loc);
+    *lhs_type = lhs->data_type;
+    *matrix_writemask = false;
 
     while (lhs->type != HLSL_IR_LOAD && lhs->type != HLSL_IR_INDEX)
     {
@@ -2210,18 +2078,19 @@ static bool add_assignment(struct hlsl_ctx *ctx, struct hlsl_block *block, struc
             if (hlsl_type_component_count(lhs->data_type) != hlsl_type_component_count(cast->data_type))
             {
                 hlsl_fixme(ctx, &cast->loc, "Size change on the LHS.");
-                return false;
+                return NULL;
             }
             if (hlsl_version_ge(ctx, 4, 0) && (!is_function_out_arg || !first_cast))
             {
                 hlsl_error(ctx, &cast->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_LVALUE,
                         "Base type casts are not allowed on the LHS for profiles >= 4.");
-                return false;
+                return NULL;
             }
 
-            lhs_type = lhs->data_type;
-            if (lhs_type->class == HLSL_CLASS_VECTOR || (lhs_type->class == HLSL_CLASS_MATRIX && matrix_writemask))
-                lhs_type = hlsl_get_vector_type(ctx, lhs->data_type->e.numeric.type, width);
+            *lhs_type = lhs->data_type;
+            if ((*lhs_type)->class == HLSL_CLASS_VECTOR
+                    || ((*lhs_type)->class == HLSL_CLASS_MATRIX && *matrix_writemask))
+                *lhs_type = hlsl_get_vector_type(ctx, lhs->data_type->e.numeric.type, width);
 
             first_cast = false;
         }
@@ -2230,7 +2099,7 @@ static bool add_assignment(struct hlsl_ctx *ctx, struct hlsl_block *block, struc
             struct hlsl_ir_swizzle *swizzle = hlsl_ir_swizzle(lhs);
             uint32_t s;
 
-            VKD3D_ASSERT(!matrix_writemask);
+            VKD3D_ASSERT(!*matrix_writemask);
 
             if (swizzle->val.node->data_type->class == HLSL_CLASS_MATRIX)
             {
@@ -2239,48 +2108,80 @@ static bool add_assignment(struct hlsl_ctx *ctx, struct hlsl_block *block, struc
                 if (swizzle->val.node->type != HLSL_IR_LOAD && swizzle->val.node->type != HLSL_IR_INDEX)
                 {
                     hlsl_fixme(ctx, &lhs->loc, "Unhandled source of matrix swizzle.");
-                    return false;
+                    return NULL;
                 }
-                if (!invert_swizzle_matrix(&ms, &s, &writemask, &width))
+                if (!invert_swizzle_matrix(&ms, &s, writemask, &width))
                 {
-                    hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_WRITEMASK, "Invalid writemask for matrix.");
-                    return false;
+                    hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_WRITEMASK,
+                            "Invalid writemask for matrix.");
+                    return NULL;
                 }
-                matrix_writemask = true;
+                *matrix_writemask = true;
             }
             else
             {
                 s = swizzle->u.vector;
-                if (!invert_swizzle(&s, &writemask, &width))
+                if (!invert_swizzle(&s, writemask, &width))
                 {
                     hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_WRITEMASK, "Invalid writemask.");
-                    return false;
+                    return NULL;
                 }
             }
 
-            rhs = hlsl_block_add_swizzle(ctx, block, s, width, rhs, &swizzle->node.loc);
+            *rhs = hlsl_block_add_swizzle(ctx, block, s, width, *rhs, &swizzle->node.loc);
             lhs = swizzle->val.node;
-            lhs_type = hlsl_get_vector_type(ctx, lhs_type->e.numeric.type, width);
+            *lhs_type = hlsl_get_vector_type(ctx, (*lhs_type)->e.numeric.type, width);
         }
         else
         {
             hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_LVALUE, "Invalid lvalue.");
-            return false;
+            return NULL;
         }
     }
 
+    return lhs;
+}
+
+static bool add_assignment(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *lhs,
+        enum parse_assign_op assign_op, struct hlsl_ir_node *rhs, bool is_function_out_arg)
+{
+    struct hlsl_type *lhs_type = lhs->data_type;
+    bool matrix_writemask = false;
+    unsigned int writemask = 0;
+
+    if (lhs->data_type->class == HLSL_CLASS_ERROR || rhs->data_type->class == HLSL_CLASS_ERROR)
+    {
+        block->value = ctx->error_instr;
+        return true;
+    }
+
+    if (assign_op == ASSIGN_OP_SUB)
+    {
+        if (!(rhs = add_unary_arithmetic_expr(ctx, block, HLSL_OP1_NEG, rhs, &rhs->loc)))
+            return false;
+        assign_op = ASSIGN_OP_ADD;
+    }
+    if (assign_op != ASSIGN_OP_ASSIGN)
+    {
+        enum hlsl_ir_expr_op op = op_from_assignment(assign_op);
+
+        VKD3D_ASSERT(op);
+        if (!(rhs = add_binary_expr(ctx, block, op, lhs, rhs, &rhs->loc)))
+            return false;
+    }
+
+    rhs = add_implicit_conversion(ctx, block, rhs, lhs_type, &rhs->loc);
+
+    if (!(lhs = resolve_assignment_lhs(ctx, block, is_function_out_arg, lhs,
+            &lhs_type, &rhs, &writemask, &matrix_writemask)))
+        return false;
+
     /* lhs casts could have resulted in a discrepancy between the
      * rhs->data_type and the type of the variable that will be ulimately
      * stored to. This is corrected. */
     rhs = add_cast(ctx, block, rhs, lhs_type, &rhs->loc);
 
-    if (lhs->type == HLSL_IR_INDEX && (resource_access = hlsl_index_chain_find_resource_access(hlsl_ir_index(lhs))))
-    {
-        if (!add_resource_store(ctx, block, hlsl_ir_index(lhs),
-                resource_access, rhs, width, writemask, matrix_writemask))
-            return false;
-    }
-    else if (matrix_writemask)
+    if (matrix_writemask)
     {
         struct hlsl_deref deref;
         unsigned int i, j, k = 0;
@@ -2420,7 +2321,7 @@ static void initialize_var_components(struct hlsl_ctx *ctx, struct hlsl_block *i
             {
                 if (!hlsl_clone_block(ctx, &block, instrs))
                     return;
-                default_value = evaluate_static_expression(ctx, &block, dst_comp_type, &src->loc);
+                default_value = evaluate_static_expression(ctx, &block, dst_comp_type, true, &src->loc);
 
                 if (dst->default_values)
                     dst->default_values[*store_index] = default_value;
@@ -2691,6 +2592,14 @@ static void declare_var(struct hlsl_ctx *ctx, struct parse_variable_def *v)
         return;
     }
 
+    if (ctx->cur_scope == ctx->globals
+            && !(modifiers & (HLSL_STORAGE_STATIC | HLSL_STORAGE_GROUPSHARED)))
+        modifiers |= HLSL_STORAGE_UNIFORM;
+
+    if (ctx->cur_scope == ctx->globals && (modifiers & HLSL_STORAGE_UNIFORM)
+            && (ctx->compatibility_flags & VKD3D_SHADER_COMPILE_OPTION_CONST_GLOBAL_UNIFORMS))
+        type = hlsl_type_clone(ctx, type, 0, HLSL_MODIFIER_CONST);
+
     if (!(var = hlsl_new_var(ctx, var_name, type, &v->loc, &new_semantic, modifiers, &v->reg_reservation)))
     {
         hlsl_cleanup_semantic(&new_semantic);
@@ -2717,12 +2626,11 @@ static void declare_var(struct hlsl_ctx *ctx, struct parse_variable_def *v)
             hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
                     "packoffset() is only allowed inside constant buffer declarations.");
     }
-    else
-    {
-        if ((type->modifiers & HLSL_MODIFIER_CONST) && !v->initializer.args_count)
-            hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_MISSING_INITIALIZER,
-                    "Const variable \"%s\" is missing an initializer.", var->name);
-    }
+
+    if (!(modifiers & (HLSL_STORAGE_UNIFORM | HLSL_STORAGE_STATIC | HLSL_STORAGE_GROUPSHARED))
+            && (type->modifiers & HLSL_MODIFIER_CONST) && !v->initializer.args_count)
+        hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_MISSING_INITIALIZER,
+                "Const variable \"%s\" is missing an initializer.", var->name);
 
     if (ctx->cur_scope == ctx->globals)
     {
@@ -2730,25 +2638,14 @@ static void declare_var(struct hlsl_ctx *ctx, struct parse_variable_def *v)
             hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
                     "Variable '%s' is declared as both \"uniform\" and \"static\".", var->name);
 
-        if ((modifiers & HLSL_STORAGE_GROUPSHARED))
-        {
-            /* d3dcompiler/fxc always validates global groupshared variables,
-             * regardless of whether the groupshared modifier is ignored. */
-            validate_groupshared_var(ctx, var);
-
-            if (ctx->profile->type != VKD3D_SHADER_TYPE_COMPUTE)
-            {
-                modifiers &= ~HLSL_STORAGE_GROUPSHARED;
-                hlsl_warning(ctx, &var->loc, VKD3D_SHADER_WARNING_HLSL_IGNORED_MODIFIER,
-                        "Ignoring the 'groupshared' modifier in a non-compute shader.");
-            }
-        }
+        if ((modifiers & HLSL_STORAGE_UNIFORM) && (modifiers & HLSL_STORAGE_GROUPSHARED))
+            hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
+                    "Variable '%s' is declared as both \"uniform\" and \"groupshared\".", var->name);
 
-        /* Mark it as uniform. We need to do this here since synthetic
-            * variables also get put in the global scope, but shouldn't be
-            * considered uniforms, and we have no way of telling otherwise. */
-        if (!(modifiers & (HLSL_STORAGE_STATIC | HLSL_STORAGE_GROUPSHARED)))
-            var->storage_modifiers |= HLSL_STORAGE_UNIFORM;
+        /* d3dcompiler/fxc always validates global groupshared variables,
+         * regardless of whether the groupshared modifier is ignored. */
+        if (modifiers & HLSL_STORAGE_GROUPSHARED)
+            validate_groupshared_var(ctx, var);
 
         if (stream_output)
             hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_MISPLACED_STREAM_OUTPUT,
@@ -2862,7 +2759,7 @@ static struct hlsl_block *initialize_vars(struct hlsl_ctx *ctx, struct list *var
 
             static_initialization = var->storage_modifiers & HLSL_STORAGE_STATIC
                     || (var->data_type->modifiers & HLSL_MODIFIER_CONST
-                    && is_static_expression(v->initializer.instrs));
+                    && is_compile_time_const(v->initializer.instrs, false));
 
             if (is_default_values_initializer)
             {
@@ -2895,6 +2792,7 @@ static struct hlsl_block *initialize_vars(struct hlsl_ctx *ctx, struct list *var
             else if (static_initialization)
             {
                 hlsl_block_add_block(&ctx->static_initializers, v->initializer.instrs);
+                var->is_compile_time_const = true;
             }
             else
             {
@@ -2919,6 +2817,7 @@ static struct hlsl_block *initialize_vars(struct hlsl_ctx *ctx, struct list *var
             zero = hlsl_block_add_uint_constant(ctx, &ctx->static_initializers, 0, &var->loc);
             cast = add_cast(ctx, &ctx->static_initializers, zero, var->data_type, &var->loc);
             hlsl_block_add_simple_store(ctx, &ctx->static_initializers, var, cast);
+            var->is_compile_time_const = true;
         }
         free_parse_variable_def(v);
     }
@@ -3276,7 +3175,7 @@ static struct hlsl_ir_node *intrinsic_float_convert_arg(struct hlsl_ctx *ctx,
     if (!hlsl_type_is_integer(type))
         return arg;
 
-    type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_FLOAT, type->e.numeric.dimx, type->e.numeric.dimy);
+    type = hlsl_change_base_type(ctx, type, HLSL_TYPE_FLOAT);
     return add_implicit_conversion(ctx, params->instrs, arg, type, loc);
 }
 
@@ -3359,7 +3258,7 @@ static bool elementwise_intrinsic_float_convert_args(struct hlsl_ctx *ctx,
     if (!(type = elementwise_intrinsic_get_common_type(ctx, params, loc)))
         return false;
     if (hlsl_type_is_integer(type))
-        type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_FLOAT, type->e.numeric.dimx, type->e.numeric.dimy);
+        type = hlsl_change_base_type(ctx, type, HLSL_TYPE_FLOAT);
 
     convert_args(ctx, params, type, loc);
     return true;
@@ -3373,7 +3272,7 @@ static bool elementwise_intrinsic_int_convert_args(struct hlsl_ctx *ctx,
     if (!(type = elementwise_intrinsic_get_common_type(ctx, params, loc)))
         return false;
 
-    type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_INT, type->e.numeric.dimx, type->e.numeric.dimy);
+    type = hlsl_change_base_type(ctx, type, HLSL_TYPE_INT);
 
     convert_args(ctx, params, type, loc);
     return true;
@@ -3387,7 +3286,7 @@ static bool elementwise_intrinsic_uint_convert_args(struct hlsl_ctx *ctx,
     if (!(type = elementwise_intrinsic_get_common_type(ctx, params, loc)))
         return false;
 
-    type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_UINT, type->e.numeric.dimx, type->e.numeric.dimy);
+    type = hlsl_change_base_type(ctx, type, HLSL_TYPE_UINT);
 
     convert_args(ctx, params, type, loc);
     return true;
@@ -3454,14 +3353,6 @@ static bool intrinsic_acos(struct hlsl_ctx *ctx,
     return write_acos_or_asin(ctx, params, loc, false);
 }
 
-/* Find the type corresponding to the given source type, with the same
- * dimensions but a different base type. */
-static struct hlsl_type *convert_numeric_type(const struct hlsl_ctx *ctx,
-        const struct hlsl_type *type, enum hlsl_base_type base_type)
-{
-    return hlsl_get_numeric_type(ctx, type->class, base_type, type->e.numeric.dimx, type->e.numeric.dimy);
-}
-
 static void add_combine_components(struct hlsl_ctx *ctx, const struct parse_initializer *params,
         struct hlsl_ir_node *arg, enum hlsl_ir_expr_op op, const struct vkd3d_shader_location *loc)
 {
@@ -3485,7 +3376,7 @@ static bool intrinsic_all(struct hlsl_ctx *ctx,
     struct hlsl_ir_node *arg = params->args[0], *cast;
     struct hlsl_type *bool_type;
 
-    bool_type = convert_numeric_type(ctx, arg->data_type, HLSL_TYPE_BOOL);
+    bool_type = hlsl_change_base_type(ctx, arg->data_type, HLSL_TYPE_BOOL);
     cast = add_cast(ctx, params->instrs, arg, bool_type, loc);
     add_combine_components(ctx, params, cast, HLSL_OP2_LOGIC_AND, loc);
     return true;
@@ -3497,7 +3388,7 @@ static bool intrinsic_any(struct hlsl_ctx *ctx, const struct parse_initializer *
     struct hlsl_ir_node *arg = params->args[0], *cast;
     struct hlsl_type *bool_type;
 
-    bool_type = convert_numeric_type(ctx, arg->data_type, HLSL_TYPE_BOOL);
+    bool_type = hlsl_change_base_type(ctx, arg->data_type, HLSL_TYPE_BOOL);
     cast = add_cast(ctx, params->instrs, arg, bool_type, loc);
     add_combine_components(ctx, params, cast, HLSL_OP2_LOGIC_OR, loc);
     return true;
@@ -3618,7 +3509,7 @@ static bool intrinsic_asfloat(struct hlsl_ctx *ctx,
                     string->buffer);
         hlsl_release_string_buffer(ctx, string);
     }
-    data_type = convert_numeric_type(ctx, data_type, HLSL_TYPE_FLOAT);
+    data_type = hlsl_change_base_type(ctx, data_type, HLSL_TYPE_FLOAT);
 
     operands[0] = params->args[0];
     return add_expr(ctx, params->instrs, HLSL_OP1_REINTERPRET, operands, data_type, loc);
@@ -3641,7 +3532,7 @@ static bool intrinsic_asint(struct hlsl_ctx *ctx,
                     string->buffer);
         hlsl_release_string_buffer(ctx, string);
     }
-    data_type = convert_numeric_type(ctx, data_type, HLSL_TYPE_INT);
+    data_type = hlsl_change_base_type(ctx, data_type, HLSL_TYPE_INT);
 
     operands[0] = params->args[0];
     return add_expr(ctx, params->instrs, HLSL_OP1_REINTERPRET, operands, data_type, loc);
@@ -3677,7 +3568,7 @@ static bool intrinsic_asuint(struct hlsl_ctx *ctx,
                     string->buffer);
         hlsl_release_string_buffer(ctx, string);
     }
-    data_type = convert_numeric_type(ctx, data_type, HLSL_TYPE_UINT);
+    data_type = hlsl_change_base_type(ctx, data_type, HLSL_TYPE_UINT);
 
     operands[0] = params->args[0];
     return add_expr(ctx, params->instrs, HLSL_OP1_REINTERPRET, operands, data_type, loc);
@@ -3785,7 +3676,7 @@ static bool intrinsic_countbits(struct hlsl_ctx *ctx,
 
     if (!elementwise_intrinsic_uint_convert_args(ctx, params, loc))
         return false;
-    type = convert_numeric_type(ctx, params->args[0]->data_type, HLSL_TYPE_UINT);
+    type = hlsl_change_base_type(ctx, params->args[0]->data_type, HLSL_TYPE_UINT);
 
     operands[0] = params->args[0];
     return add_expr(ctx, params->instrs, HLSL_OP1_COUNTBITS, operands, type, loc);
@@ -4116,7 +4007,7 @@ static bool intrinsic_f16tof32(struct hlsl_ctx *ctx,
     if (!elementwise_intrinsic_uint_convert_args(ctx, params, loc))
         return false;
 
-    type = convert_numeric_type(ctx, params->args[0]->data_type, HLSL_TYPE_FLOAT);
+    type = hlsl_change_base_type(ctx, params->args[0]->data_type, HLSL_TYPE_FLOAT);
 
     operands[0] = params->args[0];
     return add_expr(ctx, params->instrs, HLSL_OP1_F16TOF32, operands, type, loc);
@@ -4131,7 +4022,7 @@ static bool intrinsic_f32tof16(struct hlsl_ctx *ctx,
     if (!elementwise_intrinsic_float_convert_args(ctx, params, loc))
         return false;
 
-    type = convert_numeric_type(ctx, params->args[0]->data_type, HLSL_TYPE_UINT);
+    type = hlsl_change_base_type(ctx, params->args[0]->data_type, HLSL_TYPE_UINT);
 
     operands[0] = params->args[0];
     return add_expr(ctx, params->instrs, HLSL_OP1_F32TOF16, operands, type, loc);
@@ -4158,7 +4049,7 @@ static bool intrinsic_firstbithigh(struct hlsl_ctx *ctx,
         if (!elementwise_intrinsic_int_convert_args(ctx, params, loc))
             return false;
     }
-    type = convert_numeric_type(ctx, type, HLSL_TYPE_UINT);
+    type = hlsl_change_base_type(ctx, type, HLSL_TYPE_UINT);
 
     operands[0] = params->args[0];
     if (hlsl_version_lt(ctx, 5, 0))
@@ -4194,7 +4085,7 @@ static bool intrinsic_firstbitlow(struct hlsl_ctx *ctx,
 
     if (!elementwise_intrinsic_uint_convert_args(ctx, params, loc))
         return false;
-    type = convert_numeric_type(ctx, params->args[0]->data_type, HLSL_TYPE_UINT);
+    type = hlsl_change_base_type(ctx, params->args[0]->data_type, HLSL_TYPE_UINT);
 
     operands[0] = params->args[0];
     return add_expr(ctx, params->instrs, HLSL_OP1_CTZ, operands, type, loc);
@@ -4287,10 +4178,10 @@ static bool intrinsic_frexp(struct hlsl_ctx *ctx,
         hlsl_fixme(ctx, loc, "frexp() on doubles.");
         return false;
     }
-    type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_FLOAT, type->e.numeric.dimx, type->e.numeric.dimy);
-    uint_dim_type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_UINT, type->e.numeric.dimx, type->e.numeric.dimy);
-    int_dim_type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_INT, type->e.numeric.dimx, type->e.numeric.dimy);
-    bool_dim_type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_BOOL, type->e.numeric.dimx, type->e.numeric.dimy);
+    type = hlsl_change_base_type(ctx, type, HLSL_TYPE_FLOAT);
+    uint_dim_type = hlsl_change_base_type(ctx, type, HLSL_TYPE_UINT);
+    int_dim_type = hlsl_change_base_type(ctx, type, HLSL_TYPE_INT);
+    bool_dim_type = hlsl_change_base_type(ctx, type, HLSL_TYPE_BOOL);
 
     if (!(body = hlsl_sprintf_alloc(ctx, template, type->name, type->name, type->name,
             bool_dim_type->name, uint_dim_type->name, int_dim_type->name, type->name, type->name)))
@@ -4333,12 +4224,9 @@ static bool intrinsic_fwidth(struct hlsl_ctx *ctx,
 static bool intrinsic_isinf(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    struct hlsl_type *type = params->args[0]->data_type, *bool_type;
+    struct hlsl_type *bool_type = hlsl_change_base_type(ctx, params->args[0]->data_type, HLSL_TYPE_BOOL);
     struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {0};
 
-    bool_type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_BOOL,
-            type->e.numeric.dimx, type->e.numeric.dimy);
-
     args[0] = params->args[0];
     return !!add_expr(ctx, params->instrs, HLSL_OP1_ISINF, args, bool_type, loc);
 }
@@ -4816,8 +4704,7 @@ static bool intrinsic_sign(struct hlsl_ctx *ctx,
     struct hlsl_ir_node *lt, *neg, *op1, *op2, *zero, *arg = params->args[0];
     static const struct hlsl_constant_value zero_value;
 
-    struct hlsl_type *int_type = hlsl_get_numeric_type(ctx, arg->data_type->class, HLSL_TYPE_INT,
-            arg->data_type->e.numeric.dimx, arg->data_type->e.numeric.dimy);
+    struct hlsl_type *int_type = hlsl_change_base_type(ctx, arg->data_type, HLSL_TYPE_INT);
 
     zero = hlsl_block_add_constant(ctx, params->instrs,
             hlsl_get_scalar_type(ctx, arg->data_type->e.numeric.type), &zero_value, loc);
@@ -4992,10 +4879,11 @@ static bool intrinsic_tanh(struct hlsl_ctx *ctx,
 }
 
 static bool intrinsic_tex(struct hlsl_ctx *ctx, const struct parse_initializer *params,
-        const struct vkd3d_shader_location *loc, const char *name, enum hlsl_sampler_dim dim)
+        const struct vkd3d_shader_location *loc, const char *name,
+        enum hlsl_sampler_dim dim, enum hlsl_resource_load_type type)
 {
+    struct hlsl_resource_load_params load_params = {.type = type};
     unsigned int sampler_dim = hlsl_sampler_dim_count(dim);
-    struct hlsl_resource_load_params load_params = { 0 };
     const struct hlsl_type *sampler_type;
     struct hlsl_ir_node *coords;
 
@@ -5019,17 +4907,10 @@ static bool intrinsic_tex(struct hlsl_ctx *ctx, const struct parse_initializer *
         hlsl_release_string_buffer(ctx, string);
     }
 
-    if (!strcmp(name, "tex2Dbias")
-            || !strcmp(name, "tex2Dlod")
-            || !strcmp(name, "texCUBEbias"))
+    if (type == HLSL_RESOURCE_SAMPLE_LOD || type == HLSL_RESOURCE_SAMPLE_LOD_BIAS)
     {
         struct hlsl_ir_node *lod, *c;
 
-        if (!strcmp(name, "tex2Dlod"))
-            load_params.type = HLSL_RESOURCE_SAMPLE_LOD;
-        else
-            load_params.type = HLSL_RESOURCE_SAMPLE_LOD_BIAS;
-
         c = hlsl_block_add_swizzle(ctx, params->instrs, HLSL_SWIZZLE(X, Y, Z, W), sampler_dim, params->args[1], loc);
         coords = add_implicit_conversion(ctx, params->instrs, c,
                 hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc);
@@ -5038,9 +4919,7 @@ static bool intrinsic_tex(struct hlsl_ctx *ctx, const struct parse_initializer *
         load_params.lod = add_implicit_conversion(ctx, params->instrs, lod,
                 hlsl_get_scalar_type(ctx, HLSL_TYPE_FLOAT), loc);
     }
-    else if (!strcmp(name, "tex2Dproj")
-            || !strcmp(name, "tex3Dproj")
-            || !strcmp(name, "texCUBEproj"))
+    else if (type == HLSL_RESOURCE_SAMPLE_PROJ)
     {
         coords = add_implicit_conversion(ctx, params->instrs, params->args[1],
                 hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, 4), loc);
@@ -5057,12 +4936,8 @@ static bool intrinsic_tex(struct hlsl_ctx *ctx, const struct parse_initializer *
 
             load_params.type = HLSL_RESOURCE_SAMPLE;
         }
-        else
-        {
-            load_params.type = HLSL_RESOURCE_SAMPLE_PROJ;
-        }
     }
-    else if (params->args_count == 4) /* Gradient sampling. */
+    else if (type == HLSL_RESOURCE_SAMPLE_GRAD)
     {
         coords = add_implicit_conversion(ctx, params->instrs, params->args[1],
                 hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc);
@@ -5070,11 +4945,9 @@ static bool intrinsic_tex(struct hlsl_ctx *ctx, const struct parse_initializer *
                 hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc);
         load_params.ddy = add_implicit_conversion(ctx, params->instrs, params->args[3],
                 hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc);
-        load_params.type = HLSL_RESOURCE_SAMPLE_GRAD;
     }
     else
     {
-        load_params.type = HLSL_RESOURCE_SAMPLE;
         coords = add_implicit_conversion(ctx, params->instrs, params->args[1],
                 hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc);
     }
@@ -5110,85 +4983,107 @@ static bool intrinsic_tex(struct hlsl_ctx *ctx, const struct parse_initializer *
 static bool intrinsic_tex1D(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "tex1D", HLSL_SAMPLER_DIM_1D);
+    return intrinsic_tex(ctx, params, loc, "tex1D", HLSL_SAMPLER_DIM_1D,
+            params->args_count == 4 ? HLSL_RESOURCE_SAMPLE_GRAD : HLSL_RESOURCE_SAMPLE);
 }
 
 static bool intrinsic_tex1Dgrad(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "tex1Dgrad", HLSL_SAMPLER_DIM_1D);
+    return intrinsic_tex(ctx, params, loc, "tex1Dgrad", HLSL_SAMPLER_DIM_1D, HLSL_RESOURCE_SAMPLE_GRAD);
 }
 
 static bool intrinsic_tex2D(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "tex2D", HLSL_SAMPLER_DIM_2D);
+    return intrinsic_tex(ctx, params, loc, "tex2D", HLSL_SAMPLER_DIM_2D,
+            params->args_count == 4 ? HLSL_RESOURCE_SAMPLE_GRAD : HLSL_RESOURCE_SAMPLE);
 }
 
 static bool intrinsic_tex2Dbias(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "tex2Dbias", HLSL_SAMPLER_DIM_2D);
+    return intrinsic_tex(ctx, params, loc, "tex2Dbias", HLSL_SAMPLER_DIM_2D, HLSL_RESOURCE_SAMPLE_LOD_BIAS);
 }
 
 static bool intrinsic_tex2Dgrad(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "tex2Dgrad", HLSL_SAMPLER_DIM_2D);
+    return intrinsic_tex(ctx, params, loc, "tex2Dgrad", HLSL_SAMPLER_DIM_2D, HLSL_RESOURCE_SAMPLE_GRAD);
 }
 
 static bool intrinsic_tex2Dlod(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "tex2Dlod", HLSL_SAMPLER_DIM_2D);
+    return intrinsic_tex(ctx, params, loc, "tex2Dlod", HLSL_SAMPLER_DIM_2D, HLSL_RESOURCE_SAMPLE_LOD);
 }
 
 static bool intrinsic_tex2Dproj(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "tex2Dproj", HLSL_SAMPLER_DIM_2D);
+    return intrinsic_tex(ctx, params, loc, "tex2Dproj", HLSL_SAMPLER_DIM_2D, HLSL_RESOURCE_SAMPLE_PROJ);
 }
 
 static bool intrinsic_tex3D(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "tex3D", HLSL_SAMPLER_DIM_3D);
+    return intrinsic_tex(ctx, params, loc, "tex3D", HLSL_SAMPLER_DIM_3D,
+            params->args_count == 4 ? HLSL_RESOURCE_SAMPLE_GRAD : HLSL_RESOURCE_SAMPLE);
+}
+
+static bool intrinsic_tex3Dbias(struct hlsl_ctx *ctx,
+        const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
+{
+    return intrinsic_tex(ctx, params, loc, "tex3Dbias", HLSL_SAMPLER_DIM_3D, HLSL_RESOURCE_SAMPLE_LOD_BIAS);
 }
 
 static bool intrinsic_tex3Dgrad(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "tex3Dgrad", HLSL_SAMPLER_DIM_3D);
+    return intrinsic_tex(ctx, params, loc, "tex3Dgrad", HLSL_SAMPLER_DIM_3D, HLSL_RESOURCE_SAMPLE_GRAD);
+}
+
+static bool intrinsic_tex3Dlod(struct hlsl_ctx *ctx,
+        const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
+{
+    return intrinsic_tex(ctx, params, loc, "tex3Dlod", HLSL_SAMPLER_DIM_3D, HLSL_RESOURCE_SAMPLE_LOD);
 }
 
 static bool intrinsic_tex3Dproj(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "tex3Dproj", HLSL_SAMPLER_DIM_3D);
+    return intrinsic_tex(ctx, params, loc, "tex3Dproj", HLSL_SAMPLER_DIM_3D, HLSL_RESOURCE_SAMPLE_PROJ);
 }
 
 static bool intrinsic_texCUBE(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "texCUBE", HLSL_SAMPLER_DIM_CUBE);
+    return intrinsic_tex(ctx, params, loc, "texCUBE", HLSL_SAMPLER_DIM_CUBE,
+            params->args_count == 4 ? HLSL_RESOURCE_SAMPLE_GRAD : HLSL_RESOURCE_SAMPLE);
 }
 
 static bool intrinsic_texCUBEbias(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "texCUBEbias", HLSL_SAMPLER_DIM_CUBE);
+    return intrinsic_tex(ctx, params, loc, "texCUBEbias", HLSL_SAMPLER_DIM_CUBE, HLSL_RESOURCE_SAMPLE_LOD_BIAS);
 }
 
 static bool intrinsic_texCUBEgrad(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "texCUBEgrad", HLSL_SAMPLER_DIM_CUBE);
+    return intrinsic_tex(ctx, params, loc, "texCUBEgrad", HLSL_SAMPLER_DIM_CUBE, HLSL_RESOURCE_SAMPLE_GRAD);
+}
+
+static bool intrinsic_texCUBElod(struct hlsl_ctx *ctx,
+        const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
+{
+    return intrinsic_tex(ctx, params, loc, "texCUBElod", HLSL_SAMPLER_DIM_CUBE, HLSL_RESOURCE_SAMPLE_LOD);
 }
 
 static bool intrinsic_texCUBEproj(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    return intrinsic_tex(ctx, params, loc, "texCUBEproj", HLSL_SAMPLER_DIM_CUBE);
+    return intrinsic_tex(ctx, params, loc, "texCUBEproj", HLSL_SAMPLER_DIM_CUBE, HLSL_RESOURCE_SAMPLE_PROJ);
 }
 
 static bool intrinsic_transpose(struct hlsl_ctx *ctx,
@@ -5301,10 +5196,12 @@ static bool intrinsic_GetRenderTargetSampleCount(struct hlsl_ctx *ctx,
 static bool intrinsic_interlocked(struct hlsl_ctx *ctx, enum hlsl_interlocked_op op,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc, const char *name)
 {
-    struct hlsl_ir_node *interlocked, *lhs, *coords, *val, *cmp_val = NULL, *orig_val = NULL;
-    struct hlsl_type *lhs_type, *val_type;
+    struct hlsl_ir_node *interlocked, *lhs, *val, *cmp_val = NULL, *orig_val = NULL;
+    struct hlsl_type *lhs_type, *val_type, *ret_type = NULL;
     struct vkd3d_string_buffer *string;
+    unsigned int writemask, component;
     struct hlsl_deref dst_deref;
+    bool matrix_writemask;
 
     if (hlsl_version_lt(ctx, 5, 0))
         hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INCOMPATIBLE_PROFILE,
@@ -5336,6 +5233,9 @@ static bool intrinsic_interlocked(struct hlsl_ctx *ctx, enum hlsl_interlocked_op
             orig_val = params->args[2];
     }
 
+    if (orig_val)
+        ret_type = lhs_type;
+
     if (lhs_type->class != HLSL_CLASS_SCALAR || (lhs_type->e.numeric.type != HLSL_TYPE_UINT
             && lhs_type->e.numeric.type != HLSL_TYPE_INT))
     {
@@ -5358,8 +5258,10 @@ static bool intrinsic_interlocked(struct hlsl_ctx *ctx, enum hlsl_interlocked_op
         /* Floating values are always cast to signed integers. */
         if (val_base_type == HLSL_TYPE_FLOAT || val_base_type == HLSL_TYPE_HALF || val_base_type == HLSL_TYPE_DOUBLE)
             val_type = hlsl_get_scalar_type(ctx, HLSL_TYPE_INT);
+        else if (val_base_type != lhs_type->e.numeric.type)
+            val_type = hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT);
         else
-            val_type = hlsl_get_scalar_type(ctx, lhs_type->e.numeric.type);
+            val_type = hlsl_get_scalar_type(ctx, val_base_type);
     }
     else
     {
@@ -5371,47 +5273,50 @@ static bool intrinsic_interlocked(struct hlsl_ctx *ctx, enum hlsl_interlocked_op
     if (!(val = add_implicit_conversion(ctx, params->instrs, val, val_type, loc)))
         return false;
 
-    /* TODO: groupshared variables */
-    if (lhs->type == HLSL_IR_INDEX && hlsl_index_chain_find_resource_access(hlsl_ir_index(lhs)))
-    {
-        if (!hlsl_index_is_resource_access(hlsl_ir_index(lhs)))
-        {
-            hlsl_fixme(ctx, &lhs->loc, "Non-direct structured resource interlocked targets.");
-            return false;
-        }
-
-        if (!hlsl_init_deref_from_index_chain(ctx, &dst_deref, hlsl_ir_index(lhs)->val.node))
-            return false;
-        coords = hlsl_ir_index(lhs)->idx.node;
+    if (!(lhs = resolve_assignment_lhs(ctx, params->instrs, false, lhs,
+            &lhs_type, &val, &writemask, &matrix_writemask)))
+        return false;
 
-        VKD3D_ASSERT(coords->data_type->class == HLSL_CLASS_VECTOR);
-        VKD3D_ASSERT(coords->data_type->e.numeric.type == HLSL_TYPE_UINT);
+    VKD3D_ASSERT(writemask);
+    /* The writemask should be single component. */
+    VKD3D_ASSERT(!(writemask & (writemask - 1)));
 
-        if (hlsl_deref_get_type(ctx, &dst_deref)->class != HLSL_CLASS_UAV)
-        {
-            hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
-                    "Interlocked targets must be UAV or groupshared elements.");
-            return false;
-        }
-    }
-    else if (lhs->type == HLSL_IR_INDEX && hlsl_index_chain_has_tgsm_access(hlsl_ir_index(lhs)))
+    component = vkd3d_log2i(writemask);
+    if (matrix_writemask)
     {
-        hlsl_fixme(ctx, loc, "Interlocked operations on indexed groupshared elements.");
-        return false;
+        unsigned int i = component / 4, j = component % 4;
+
+        component = i * lhs->data_type->e.numeric.dimx + j;
     }
-    else if (lhs->type == HLSL_IR_LOAD && (hlsl_ir_load(lhs)->src.var->storage_modifiers & HLSL_STORAGE_GROUPSHARED))
+
+    if (lhs->type == HLSL_IR_INDEX && hlsl_index_is_noncontiguous(hlsl_ir_index(lhs)))
     {
-        hlsl_init_simple_deref_from_var(&dst_deref, hlsl_ir_load(lhs)->src.var);
-        coords = hlsl_block_add_uint_constant(ctx, params->instrs, 0, loc);
+        struct hlsl_ir_node *c, *cell;
+
+        VKD3D_ASSERT(!matrix_writemask);
+
+        c = hlsl_block_add_uint_constant(ctx, params->instrs, component, &lhs->loc);
+        cell = hlsl_block_add_index(ctx, params->instrs, lhs, c, &lhs->loc);
+
+        if (!hlsl_init_deref_from_index_chain(ctx, &dst_deref, cell))
+            return false;
     }
     else
     {
-        hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
-                "Interlocked targets must be UAV or groupshared elements.");
-        return false;
+        struct hlsl_block component_path_block;
+        struct hlsl_deref dst_deref_prefix;
+
+        if (!hlsl_init_deref_from_index_chain(ctx, &dst_deref_prefix, lhs))
+            return false;
+        if (!hlsl_init_deref_from_component_index(ctx, &component_path_block, &dst_deref,
+                &dst_deref_prefix, component, &lhs->loc))
+            return false;
+
+        hlsl_block_add_block(params->instrs, &component_path_block);
+        hlsl_cleanup_deref(&dst_deref_prefix);
     }
 
-    interlocked = hlsl_new_interlocked(ctx, op, orig_val ? lhs_type : NULL, &dst_deref, coords, cmp_val, val, loc);
+    interlocked = hlsl_new_interlocked(ctx, op, ret_type, &dst_deref, NULL, cmp_val, val, loc);
     hlsl_cleanup_deref(&dst_deref);
     if (!interlocked)
         return false;
@@ -5485,19 +5390,9 @@ static bool intrinsic_InterlockedXor(struct hlsl_ctx *ctx,
     return intrinsic_interlocked(ctx, HLSL_INTERLOCKED_XOR, params, loc, "InterlockedXor");
 }
 
-static void validate_group_barrier_profile(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc)
-{
-    if (ctx->profile->type != VKD3D_SHADER_TYPE_COMPUTE)
-    {
-        hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INCOMPATIBLE_PROFILE,
-                "Group barriers can only be used in compute shaders.");
-    }
-}
-
 static bool intrinsic_AllMemoryBarrier(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    validate_group_barrier_profile(ctx, loc);
     return !!hlsl_block_add_sync(ctx, params->instrs, VKD3DSSF_GLOBAL_UAV
             | VKD3DSSF_GROUP_SHARED_MEMORY, loc);
 }
@@ -5505,7 +5400,6 @@ static bool intrinsic_AllMemoryBarrier(struct hlsl_ctx *ctx,
 static bool intrinsic_AllMemoryBarrierWithGroupSync(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    validate_group_barrier_profile(ctx, loc);
     return !!hlsl_block_add_sync(ctx, params->instrs, VKD3DSSF_GLOBAL_UAV
             | VKD3DSSF_GROUP_SHARED_MEMORY | VKD3DSSF_THREAD_GROUP, loc);
 }
@@ -5627,19 +5521,12 @@ static bool intrinsic_ConstructGSWithSO(struct hlsl_ctx *ctx,
 static bool intrinsic_DeviceMemoryBarrier(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    if ((ctx->profile->type != VKD3D_SHADER_TYPE_COMPUTE && ctx->profile->type != VKD3D_SHADER_TYPE_PIXEL)
-            || hlsl_version_lt(ctx, 4, 0))
-    {
-        hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INCOMPATIBLE_PROFILE,
-                "DeviceMemoryBarrier() can only be used in compute and pixel shaders 4.0 or higher.");
-    }
     return !!hlsl_block_add_sync(ctx, params->instrs, VKD3DSSF_GLOBAL_UAV, loc);
 }
 
 static bool intrinsic_DeviceMemoryBarrierWithGroupSync(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    validate_group_barrier_profile(ctx, loc);
     return !!hlsl_block_add_sync(ctx, params->instrs, VKD3DSSF_GLOBAL_UAV
             | VKD3DSSF_THREAD_GROUP, loc);
 }
@@ -5647,7 +5534,6 @@ static bool intrinsic_DeviceMemoryBarrierWithGroupSync(struct hlsl_ctx *ctx,
 static bool intrinsic_GroupMemoryBarrier(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    validate_group_barrier_profile(ctx, loc);
     return !!hlsl_block_add_sync(ctx, params->instrs,
             VKD3DSSF_GROUP_SHARED_MEMORY, loc);
 }
@@ -5655,7 +5541,6 @@ static bool intrinsic_GroupMemoryBarrier(struct hlsl_ctx *ctx,
 static bool intrinsic_GroupMemoryBarrierWithGroupSync(struct hlsl_ctx *ctx,
         const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
 {
-    validate_group_barrier_profile(ctx, loc);
     return !!hlsl_block_add_sync(ctx, params->instrs,
             VKD3DSSF_GROUP_SHARED_MEMORY | VKD3DSSF_THREAD_GROUP, loc);
 }
@@ -5769,11 +5654,14 @@ intrinsic_functions[] =
     {"tex2Dlod",                            2, false, intrinsic_tex2Dlod},
     {"tex2Dproj",                           2, false, intrinsic_tex2Dproj},
     {"tex3D",                              -1, false, intrinsic_tex3D},
+    {"tex3Dbias",                           2, false, intrinsic_tex3Dbias},
     {"tex3Dgrad",                           4, false, intrinsic_tex3Dgrad},
+    {"tex3Dlod",                            2, false, intrinsic_tex3Dlod},
     {"tex3Dproj",                           2, false, intrinsic_tex3Dproj},
     {"texCUBE",                            -1, false, intrinsic_texCUBE},
     {"texCUBEbias",                         2, false, intrinsic_texCUBEbias},
     {"texCUBEgrad",                         4, false, intrinsic_texCUBEgrad},
+    {"texCUBElod",                          2, false, intrinsic_texCUBElod},
     {"texCUBEproj",                         2, false, intrinsic_texCUBEproj},
     {"transpose",                           1, true,  intrinsic_transpose},
     {"trunc",                               1, true,  intrinsic_trunc},
@@ -5978,16 +5866,14 @@ static bool add_ternary(struct hlsl_ctx *ctx, struct hlsl_block *block,
 
         if (cond_type->e.numeric.dimx == 1 && cond_type->e.numeric.dimy == 1)
         {
-            cond_type = hlsl_get_numeric_type(ctx, common_type->class,
-                    HLSL_TYPE_BOOL, common_type->e.numeric.dimx, common_type->e.numeric.dimy);
+            cond_type = hlsl_change_base_type(ctx, common_type, HLSL_TYPE_BOOL);
             cond = add_implicit_conversion(ctx, block, cond, cond_type, &cond->loc);
         }
         else
         {
             if (common_type->e.numeric.dimx == 1 && common_type->e.numeric.dimy == 1)
             {
-                common_type = hlsl_get_numeric_type(ctx, cond_type->class,
-                        common_type->e.numeric.type, cond_type->e.numeric.dimx, cond_type->e.numeric.dimy);
+                common_type = hlsl_change_base_type(ctx, cond_type, common_type->e.numeric.type);
             }
             else if (cond_type->e.numeric.dimx != common_type->e.numeric.dimx
                     || cond_type->e.numeric.dimy != common_type->e.numeric.dimy)
@@ -6007,8 +5893,7 @@ static bool add_ternary(struct hlsl_ctx *ctx, struct hlsl_block *block,
                 hlsl_release_string_buffer(ctx, value_string);
             }
 
-            cond_type = hlsl_get_numeric_type(ctx, common_type->class, HLSL_TYPE_BOOL,
-                    common_type->e.numeric.dimx, common_type->e.numeric.dimy);
+            cond_type = hlsl_change_base_type(ctx, common_type, HLSL_TYPE_BOOL);
             cond = add_implicit_conversion(ctx, block, cond, cond_type, &cond->loc);
         }
 
@@ -6031,8 +5916,7 @@ static bool add_ternary(struct hlsl_ctx *ctx, struct hlsl_block *block,
             hlsl_release_string_buffer(ctx, second_string);
         }
 
-        cond_type = hlsl_get_numeric_type(ctx, cond_type->class, HLSL_TYPE_BOOL,
-                cond_type->e.numeric.dimx, cond_type->e.numeric.dimy);
+        cond_type = hlsl_change_base_type(ctx, cond_type, HLSL_TYPE_BOOL);
         cond = add_implicit_conversion(ctx, block, cond, cond_type, &cond->loc);
         common_type = first->data_type;
     }
@@ -9482,7 +9366,7 @@ selection_statement:
             check_condition_type(ctx, condition);
 
             condition = add_cast(ctx, $4, condition, hlsl_get_scalar_type(ctx, HLSL_TYPE_BOOL), &@4);
-            hlsl_block_add_if(ctx, $4, condition, $6.then_block, $6.else_block, flatten_type, &@2);
+            hlsl_block_add_if(ctx, $4, condition, $6.then_block, $6.else_block, flatten_type, false, &@2);
 
             destroy_block($6.then_block);
             destroy_block($6.else_block);
diff --git a/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c
index 6f61eb92a67e..d10a4ff1ceb8 100644
--- a/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c
+++ b/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c
@@ -24,6 +24,8 @@
 #include <stdio.h>
 #include <math.h>
 
+VKD3D_DECLARE_DEBUG_CHANNEL(copyprop)
+
 /* The shift that corresponds to the D3D_SIF_TEXTURE_COMPONENTS mask. */
 #define VKD3D_SM4_SIF_TEXTURE_COMPONENTS_SHIFT 2
 
@@ -580,7 +582,7 @@ static void prepend_input_copy(struct hlsl_ctx *ctx, struct list *semantic_vars,
 
     for (i = 0; i < hlsl_type_major_size(type); ++i)
     {
-        struct hlsl_ir_node *cast;
+        struct hlsl_ir_node *cast, *instr;
         struct hlsl_ir_var *input;
         struct hlsl_ir_load *load;
 
@@ -606,6 +608,33 @@ static void prepend_input_copy(struct hlsl_ctx *ctx, struct list *semantic_vars,
             if (!(load = hlsl_new_load_index(ctx, &prim_deref, idx, loc)))
                 return;
             hlsl_block_add_instr(block, &load->node);
+            instr = &load->node;
+        }
+        else if (ctx->profile->type == VKD3D_SHADER_TYPE_PIXEL && hlsl_version_ge(ctx, 4, 0)
+                && (ctx->compatibility_flags & VKD3D_SHADER_COMPILE_OPTION_BACKCOMPAT_MAP_SEMANTIC_NAMES)
+                && !ascii_strcasecmp(semantic->name, "VFACE"))
+        {
+            /* VFACE on sm4 has sm3 semantics, returning 1.0 or -1.0.
+             * Also, the variable can be declared as float (which is not true
+             * of SV_IsFrontFace), but is converted to uint in the signature.
+             * Note that VPOS is not similarly affected,
+             * despite also having different semantics between sm3 and sm4. */
+
+            struct hlsl_ir_node *one, *minusone;
+
+            if (!(input = add_semantic_var(ctx, semantic_vars, var,
+                    hlsl_change_base_type(ctx, vector_type_src, HLSL_TYPE_BOOL),
+                    modifiers, semantic, 0, false, force_align, true, loc)))
+                return;
+            ++semantic->index;
+
+            if (!(load = hlsl_new_var_load(ctx, input, &var->loc)))
+                return;
+            hlsl_block_add_instr(block, &load->node);
+
+            one = hlsl_block_add_float_constant(ctx, block, 1.0f, &var->loc);
+            minusone = hlsl_block_add_float_constant(ctx, block, -1.0f, &var->loc);
+            instr = hlsl_add_conditional(ctx, block, &load->node, one, minusone);
         }
         else
         {
@@ -617,9 +646,10 @@ static void prepend_input_copy(struct hlsl_ctx *ctx, struct list *semantic_vars,
             if (!(load = hlsl_new_var_load(ctx, input, &var->loc)))
                 return;
             hlsl_block_add_instr(block, &load->node);
+            instr = &load->node;
         }
 
-        cast = hlsl_block_add_cast(ctx, block, &load->node, vector_type_dst, &var->loc);
+        cast = hlsl_block_add_cast(ctx, block, instr, vector_type_dst, &var->loc);
 
         if (type->class == HLSL_CLASS_MATRIX)
         {
@@ -1107,7 +1137,7 @@ static void insert_early_return_break(struct hlsl_ctx *ctx,
 
     hlsl_block_add_jump(ctx, &then_block, HLSL_IR_JUMP_BREAK, NULL, &cf_instr->loc);
 
-    if (!(iff = hlsl_new_if(ctx, &load->node, &then_block, NULL, HLSL_IF_FLATTEN_DEFAULT, &cf_instr->loc)))
+    if (!(iff = hlsl_new_if(ctx, &load->node, &then_block, NULL, HLSL_IF_FLATTEN_DEFAULT, false, &cf_instr->loc)))
         return;
     list_add_after(&load->node.entry, &iff->entry);
 }
@@ -1292,7 +1322,7 @@ static bool lower_return(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *fun
 
         load = hlsl_block_add_simple_load(ctx, block, func->early_return_var, &cf_instr->loc);
         not = hlsl_block_add_unary_expr(ctx, block, HLSL_OP1_LOGIC_NOT, load, &cf_instr->loc);
-        hlsl_block_add_if(ctx, block, not, &then_block, NULL, HLSL_IF_FLATTEN_DEFAULT, &cf_instr->loc);
+        hlsl_block_add_if(ctx, block, not, &then_block, NULL, HLSL_IF_FLATTEN_DEFAULT, false, &cf_instr->loc);
     }
 
     return has_early_return;
@@ -1814,34 +1844,280 @@ static struct hlsl_ir_node *lower_tgsm_loads(struct hlsl_ctx *ctx, struct hlsl_i
     return hlsl_block_add_resource_load(ctx, block, &params, loc);
 }
 
-/* Lowers stores to TGSMs to resource stores. */
-static struct hlsl_ir_node *lower_tgsm_stores(struct hlsl_ctx *ctx,
+static enum vkd3d_result resource_access_from_deref(struct hlsl_ctx *ctx, struct hlsl_block *block,
+        const struct hlsl_deref *deref, struct hlsl_deref *resource_deref,
+        struct hlsl_type **resource_type, struct hlsl_type **val_type, struct hlsl_ir_node **coords,
+        struct hlsl_ir_node **field_offset, const struct vkd3d_shader_location *loc)
+{
+    bool tgsm = deref->var->is_tgsm;
+    struct hlsl_type **deref_types;
+    unsigned int resource_idx;
+
+    if (!(deref_types = vkd3d_malloc(sizeof(*deref_types) * (deref->path_len + 1))))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+
+    deref_types[0] = deref->var->data_type;
+    for (unsigned int i = 1; i <= deref->path_len; ++i)
+        deref_types[i] = hlsl_get_element_type_from_path_index(ctx, deref_types[i - 1], deref->path[i - 1].node);
+
+    *resource_type = NULL;
+    *val_type = deref_types[deref->path_len];
+
+    if (tgsm)
+    {
+        *resource_type = deref_types[0];
+        resource_idx = 0;
+    }
+    else
+    {
+        for (unsigned int i = 0; i <= deref->path_len; ++i)
+        {
+            if (deref_types[i]->class == HLSL_CLASS_UAV || deref_types[i]->class == HLSL_CLASS_TEXTURE)
+            {
+                *resource_type = deref_types[i];
+                resource_idx = i;
+                break;
+            }
+        }
+    }
+
+    if (!*resource_type)
+    {
+        vkd3d_free(deref_types);
+        return VKD3D_ERROR_NOT_FOUND;
+    }
+
+    if (!hlsl_init_deref(ctx, resource_deref, deref->var, resource_idx))
+    {
+        vkd3d_free(deref_types);
+        return VKD3D_ERROR;
+    }
+
+    for (unsigned int i = 0; i < resource_idx; ++i)
+        hlsl_src_from_node(&resource_deref->path[i], deref->path[i].node);
+
+    if (tgsm)
+    {
+        if ((*resource_type)->class == HLSL_CLASS_ARRAY)
+        {
+            VKD3D_ASSERT(deref->path_len);
+
+            *coords = deref->path[0].node;
+            VKD3D_ASSERT(hlsl_is_vec1((*coords)->data_type));
+            VKD3D_ASSERT((*coords)->data_type->e.numeric.type == HLSL_TYPE_UINT);
+        }
+        else
+        {
+            *coords = hlsl_block_add_uint_constant(ctx, block, 0, loc);
+        }
+    }
+    else
+    {
+        *coords = deref->path[resource_idx].node;
+        VKD3D_ASSERT((*coords)->data_type->class == HLSL_CLASS_VECTOR);
+        VKD3D_ASSERT((*coords)->data_type->e.numeric.type == HLSL_TYPE_UINT);
+        VKD3D_ASSERT((*coords)->data_type->e.numeric.dimx == hlsl_sampler_dim_count((*resource_type)->sampler_dim));
+    }
+
+    if (tgsm || (*resource_type)->sampler_dim == HLSL_SAMPLER_DIM_STRUCTURED_BUFFER)
+    {
+        *field_offset = hlsl_block_add_uint_constant(ctx, block, 0, loc);
+        for (int i = deref->path_len - 1; i >= (int)resource_idx; --i)
+        {
+            /* The coords field is used to index resources and arrayed TGSM objects. */
+            if (i == resource_idx && (!tgsm || (*resource_type)->class == HLSL_CLASS_ARRAY))
+                break;
+
+            *field_offset = hlsl_block_add_packed_index_offset_append(ctx, block, *field_offset,
+                    deref->path[i].node, deref_types[i], loc);
+        }
+    }
+    else
+    {
+        *field_offset = NULL;
+    }
+
+    vkd3d_free(deref_types);
+    return VKD3D_OK;
+}
+
+/* Lowers stores to resources and TGSM objects to resource stores. */
+static struct hlsl_ir_node *lower_resource_stores(struct hlsl_ctx *ctx,
         struct hlsl_ir_node *instr, struct hlsl_block *block)
 {
+    struct hlsl_ir_node *coords, *field_offset;
+    struct hlsl_type *resource_type, *val_type;
+    struct hlsl_deref resource_deref = {0};
+    struct hlsl_ir_node *res = NULL;
     struct hlsl_ir_store *store;
-    struct hlsl_ir_node *coords;
-    struct hlsl_deref res_deref;
     struct hlsl_deref *deref;
+    bool tgsm;
 
     if (instr->type != HLSL_IR_STORE)
         return NULL;
     store = hlsl_ir_store(instr);
     deref = &store->lhs;
+    tgsm = deref->var->is_tgsm;
 
-    if (!deref->var->is_tgsm)
+    if (!tgsm && !(deref->var->is_uniform && deref->path_len))
         return NULL;
 
-    if (deref->path_len)
+    if (resource_access_from_deref(ctx, block, deref, &resource_deref, &resource_type,
+            &val_type, &coords, &field_offset, &instr->loc) != VKD3D_OK)
+        return NULL;
+
+    if (resource_type->class == HLSL_CLASS_TEXTURE)
+    {
+        hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                "Read-only resources cannot be stored to.");
+        return false;
+    }
+
+    if (tgsm || resource_type->sampler_dim == HLSL_SAMPLER_DIM_STRUCTURED_BUFFER)
+    {
+        unsigned int dimx = val_type->e.numeric.dimx;
+
+        VKD3D_ASSERT(val_type->class <= HLSL_CLASS_VECTOR);
+
+        for (unsigned int i = 0, k = 0; i < dimx; ++i)
+        {
+            struct hlsl_ir_node *c, *load, *offset;
+
+            if (!(store->writemask & (1u << i)))
+                continue;
+
+            if (val_type->class == HLSL_CLASS_SCALAR)
+            {
+                VKD3D_ASSERT(i == 0);
+                VKD3D_ASSERT(dimx == 1);
+
+                offset = field_offset;
+            }
+            else
+            {
+                c = hlsl_block_add_uint_constant(ctx, block, i, &instr->loc);
+                offset = hlsl_block_add_packed_index_offset_append(ctx, block, field_offset, c, val_type, &instr->loc);
+            }
+
+            load = hlsl_add_load_component(ctx, block, store->rhs.node, k++, &instr->loc);
+            res = hlsl_block_add_resource_store(ctx, block, HLSL_RESOURCE_STORE, &resource_deref,
+                    offset, coords, load, VKD3DSP_WRITEMASK_0, &instr->loc);
+        }
+    }
+    else
+    {
+        unsigned int expected_width = resource_type->e.resource.format->e.numeric.dimx
+                * resource_type->e.resource.format->e.numeric.dimy;
+        unsigned int width = vkd3d_log2i(store->writemask) + 1;
+
+        if (width != expected_width)
+            hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_WRITEMASK,
+                    "Resource store expressions must write to all components.");
+
+        res = hlsl_block_add_resource_store(ctx, block, HLSL_RESOURCE_STORE, &resource_deref,
+                NULL, coords, store->rhs.node, store->writemask, &instr->loc);
+    }
+
+    hlsl_cleanup_deref(&resource_deref);
+    return res;
+}
+
+/* Generates the coords field of an interlocked operation from its dst deref,
+ * and points the dst deref to the UAV/TGSM object itself. */
+static struct hlsl_ir_node *generate_interlocked_coords(struct hlsl_ctx *ctx,
+        struct hlsl_ir_node *instr, struct hlsl_block *block)
+{
+    struct hlsl_type *resource_type, *val_type;
+    struct hlsl_ir_node *coords, *field_offset;
+    struct hlsl_ir_interlocked *interlocked;
+    struct hlsl_deref resource_deref;
+    struct hlsl_ir_node *res = NULL;
+    struct hlsl_deref *deref;
+    enum vkd3d_result ret;
+    bool tgsm;
+
+    if (instr->type != HLSL_IR_INTERLOCKED)
+        return NULL;
+    interlocked = hlsl_ir_interlocked(instr);
+    deref = &interlocked->dst;
+    tgsm = deref->var->is_tgsm;
+
+    ret = resource_access_from_deref(ctx, block, deref, &resource_deref, &resource_type,
+            &val_type, &coords, &field_offset, &instr->loc);
+    if (ret != VKD3D_OK)
     {
-        hlsl_fixme(ctx, &instr->loc, "Store to indexed TGSM.");
+        if (ret == VKD3D_ERROR_NOT_FOUND)
+            hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                    "Interlocked targets must be UAV or groupshared elements.");
         return NULL;
     }
 
-    hlsl_init_simple_deref_from_var(&res_deref, deref->var);
-    coords = hlsl_block_add_uint_constant(ctx, block, 0, &instr->loc);
+    if (resource_type->class != HLSL_CLASS_UAV && !tgsm)
+    {
+        hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                    "Interlocked targets must be UAV or groupshared elements.");
+        goto done;
+    }
+
+    VKD3D_ASSERT(!interlocked->coords.node);
+
+    if (resource_type->sampler_dim == HLSL_SAMPLER_DIM_STRUCTURED_BUFFER
+            || (tgsm && resource_type->class == HLSL_CLASS_ARRAY))
+    {
+        struct hlsl_deref structured_coords_deref;
+        struct hlsl_ir_var *structured_coords;
+
+        if (!(structured_coords = hlsl_new_synthetic_var(ctx, "interlocked-coords",
+                hlsl_get_vector_type(ctx, HLSL_TYPE_UINT, 2), &instr->loc)))
+            goto done;
+
+        hlsl_init_simple_deref_from_var(&structured_coords_deref, structured_coords);
+
+        hlsl_block_add_store_component(ctx, block, &structured_coords_deref, 0, coords);
+        hlsl_block_add_store_component(ctx, block, &structured_coords_deref, 1, field_offset);
+
+        hlsl_cleanup_deref(&structured_coords_deref);
+
+        coords = hlsl_block_add_simple_load(ctx, block, structured_coords, &instr->loc);
+    }
+    else
+    {
+        if (tgsm)
+            coords = field_offset;
+        else
+        {
+            VKD3D_ASSERT(!field_offset);
+
+            if (resource_type->e.resource.format->class != HLSL_CLASS_SCALAR)
+            {
+                hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                        "Non-structured UAV interlocked targets must have scalar type.");
+                goto done;
+            }
+        }
+    }
+
+    if ((res = hlsl_clone_instr(ctx, instr)))
+    {
+        struct hlsl_ir_interlocked *new_interlocked = hlsl_ir_interlocked(res);
+
+        hlsl_cleanup_deref(&new_interlocked->dst);
+
+        if (!hlsl_copy_deref(ctx, &new_interlocked->dst, &resource_deref))
+        {
+            hlsl_free_instr(res);
+            res = NULL;
+            goto done;
+        }
+
+        hlsl_src_from_node(&new_interlocked->coords, coords);
+
+        hlsl_block_add_instr(block, res);
+    }
 
-    return hlsl_block_add_resource_store(ctx, block, HLSL_RESOURCE_STORE, &res_deref,
-            NULL, coords, store->rhs.node, store->writemask, &instr->loc);
+done:
+    hlsl_cleanup_deref(&resource_deref);
+    return res;
 }
 
 /* Allocate a unique, ordered index to each instruction, which will be used for
@@ -2109,7 +2385,7 @@ static void copy_propagation_invalidate_variable(struct hlsl_ctx *ctx, struct co
 {
     unsigned i;
 
-    TRACE("Invalidate variable %s[%u]%s.\n", var_def->var->name, comp, debug_hlsl_writemask(writemask));
+    TRACE_(copyprop)("Invalidate variable %s[%u]%s.\n", var_def->var->name, comp, debug_hlsl_writemask(writemask));
 
     for (i = 0; i < 4; ++i)
     {
@@ -2206,7 +2482,7 @@ static void copy_propagation_set_value(struct hlsl_ctx *ctx, struct copy_propaga
         {
             struct copy_propagation_component_trace *trace = &var_def->traces[comp + i];
 
-            TRACE("Variable %s[%u] is written by instruction %p%s.\n",
+            TRACE_(copyprop)("Variable %s[%u] is written by instruction %p%s.\n",
                     var_def->var->name, comp + i, instr, debug_hlsl_writemask(1u << i));
 
             copy_propagation_trace_record_value(ctx, trace, instr, j++, time);
@@ -2243,14 +2519,14 @@ static bool copy_propagation_replace_with_single_instr(struct hlsl_ctx *ctx,
         }
         else if (new_instr != value->node)
         {
-            TRACE("No single source for propagating load from %s[%u-%u]%s\n",
+            TRACE_(copyprop)("No single source for propagating load from %s[%u-%u]%s\n",
                     var->name, start, start + count, debug_hlsl_swizzle(swizzle, instr_component_count));
             return false;
         }
         hlsl_swizzle_set_component(&ret_swizzle, i, value->component);
     }
 
-    TRACE("Load from %s[%u-%u]%s propagated as instruction %p%s.\n",
+    TRACE_(copyprop)("Load from %s[%u-%u]%s propagated as instruction %p%s.\n",
             var->name, start, start + count, debug_hlsl_swizzle(swizzle, instr_component_count),
             new_instr, debug_hlsl_swizzle(ret_swizzle, instr_component_count));
 
@@ -2298,7 +2574,7 @@ static bool copy_propagation_replace_with_constant_vector(struct hlsl_ctx *ctx,
         return false;
     list_add_before(&instr->entry, &cons->entry);
 
-    TRACE("Load from %s[%u-%u]%s turned into a constant %p.\n",
+    TRACE_(copyprop)("Load from %s[%u-%u]%s turned into a constant %p.\n",
             var->name, start, start + count, debug_hlsl_swizzle(swizzle, instr_component_count), cons);
 
     hlsl_replace_node(instr, cons);
@@ -2482,7 +2758,7 @@ static bool copy_propagation_replace_with_deref(struct hlsl_ctx *ctx,
 
         if (hlsl_version_lt(ctx, 4, 0) && x->is_uniform && ctx->profile->type != VKD3D_SHADER_TYPE_VERTEX)
         {
-            TRACE("Skipping propagating non-constant deref to SM1 uniform %s.\n", var->name);
+            TRACE_(copyprop)("Skipping propagating non-constant deref to SM1 uniform %s.\n", var->name);
             goto done;
         }
 
@@ -2582,7 +2858,7 @@ static bool copy_propagation_replace_with_deref(struct hlsl_ctx *ctx,
     if (new_instr->data_type->class == HLSL_CLASS_SCALAR || new_instr->data_type->class == HLSL_CLASS_VECTOR)
         new_instr = hlsl_block_add_swizzle(ctx, &block, ret_swizzle, instr_component_count, new_instr, &instr->loc);
 
-    if (TRACE_ON())
+    if (TRACE_ON_(copyprop))
     {
         struct vkd3d_string_buffer buffer;
 
@@ -2608,7 +2884,7 @@ static bool copy_propagation_replace_with_deref(struct hlsl_ctx *ctx,
         vkd3d_string_buffer_printf(&buffer, "]%s (i = %p).\n",
                 debug_hlsl_swizzle(ret_swizzle, instr_component_count), index);
 
-        vkd3d_string_buffer_trace(&buffer);
+        TRACE_TEXT_(copyprop, buffer.buffer, buffer.content_size);
         vkd3d_string_buffer_cleanup(&buffer);
     }
 
@@ -2748,7 +3024,7 @@ static bool copy_propagation_transform_object_load(struct hlsl_ctx *ctx,
      */
     if (!load->src.var->is_uniform)
     {
-        TRACE("Ignoring load from non-uniform object variable %s\n", load->src.var->name);
+        TRACE_(copyprop)("Ignoring load from non-uniform object variable %s\n", load->src.var->name);
         return false;
     }
 
@@ -3815,41 +4091,34 @@ static void split_copy(struct hlsl_ctx *ctx, struct hlsl_ir_store *store,
 static void split_resource_load(struct hlsl_ctx *ctx, struct hlsl_ir_store *store,
         struct hlsl_ir_resource_load *load, const unsigned int idx, struct hlsl_type *type)
 {
-    struct hlsl_ir_node *c, *idx_offset, *split_load;
+    struct hlsl_ir_resource_load *res_load;
+    struct hlsl_ir_node *c, *idx_offset;
     struct hlsl_block block;
 
     hlsl_block_init(&block);
 
     c = hlsl_block_add_uint_constant(ctx, &block, idx, &store->node.loc);
 
-    /* Structured (i.e. arrayed) TGSM load. */
-    if (load->resource.var->is_tgsm && !load->resource.path_len && load->node.data_type->class == HLSL_CLASS_ARRAY)
-    {
-        struct hlsl_resource_load_params params = {0};
+    res_load = hlsl_ir_resource_load(hlsl_clone_instr(ctx, &load->node));
+    res_load->node.data_type = type;
 
-        params.type = HLSL_RESOURCE_LOAD;
-        params.resource = &load->node;
-        params.coords = c;
-        params.format = load->node.data_type->e.array.type;
-        split_load = hlsl_block_add_resource_load(ctx, &block, &params, &load->node.loc);
+    if (load->resource.var->is_tgsm && !load->coords.node && !load->resource.path_len
+            && load->node.data_type->class == HLSL_CLASS_ARRAY)
+    {
+        /* Structured (i.e. arrayed) TGSM load. */
+        hlsl_src_from_node(&res_load->coords, c);
     }
     else
     {
-        struct hlsl_ir_resource_load *res_load;
-
         idx_offset = hlsl_block_add_packed_index_offset_append(ctx, &block,
                 load->byte_offset.node, c, load->node.data_type, &store->node.loc);
 
-        res_load = hlsl_ir_resource_load(hlsl_clone_instr(ctx, &load->node));
         hlsl_src_remove(&res_load->byte_offset);
         hlsl_src_from_node(&res_load->byte_offset, idx_offset);
-        res_load->node.data_type = type;
-        hlsl_block_add_instr(&block, &res_load->node);
-
-        split_load = &res_load->node;
     }
 
-    hlsl_block_add_store_index(ctx, &block, &store->lhs, c, split_load, 0, &store->node.loc);
+    hlsl_block_add_instr(&block, &res_load->node);
+    hlsl_block_add_store_index(ctx, &block, &store->lhs, c, &res_load->node, 0, &store->node.loc);
     list_move_before(&store->node.entry, &block.instrs);
 }
 
@@ -3986,10 +4255,11 @@ static bool split_matrix_copies(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr
     if (rhs->type == HLSL_IR_RESOURCE_LOAD)
     {
         /* As we forbid non-scalar or vector types in non-structured resource
-         * loads, this is specific to structured buffer loads. */
+         * loads, this is specific to structured buffer loads and TGSM loads. */
         struct hlsl_ir_resource_load *load = hlsl_ir_resource_load(rhs);
 
-        VKD3D_ASSERT(hlsl_deref_get_type(ctx, &load->resource)->sampler_dim == HLSL_SAMPLER_DIM_STRUCTURED_BUFFER);
+        VKD3D_ASSERT(load->resource.var->is_tgsm
+                || hlsl_deref_get_type(ctx, &load->resource)->sampler_dim == HLSL_SAMPLER_DIM_STRUCTURED_BUFFER);
 
         for (i = 0; i < hlsl_type_major_size(type); ++i)
         {
@@ -4598,9 +4868,10 @@ static bool normalize_switch_cases(struct hlsl_ctx *ctx, struct hlsl_ir_node *in
     return true;
 }
 
-static struct hlsl_ir_node *lower_nonconstant_vector_derefs(struct hlsl_ctx *ctx,
+static struct hlsl_ir_node *lower_vector_derefs(struct hlsl_ctx *ctx,
         struct hlsl_ir_node *instr, struct hlsl_block *block)
 {
+    struct hlsl_ir_load *vector_load;
     struct hlsl_ir_node *idx;
     struct hlsl_deref *deref;
     struct hlsl_type *type;
@@ -4619,20 +4890,32 @@ static struct hlsl_ir_node *lower_nonconstant_vector_derefs(struct hlsl_ctx *ctx
     for (i = 0; i < deref->path_len - 1; ++i)
         type = hlsl_get_element_type_from_path_index(ctx, type, deref->path[i].node);
 
+    if (type->class != HLSL_CLASS_VECTOR)
+        return NULL;
+
     idx = deref->path[deref->path_len - 1].node;
 
-    if (type->class == HLSL_CLASS_VECTOR && idx->type != HLSL_IR_CONSTANT)
+    if (!(vector_load = hlsl_new_load_parent(ctx, deref, &instr->loc)))
+        return NULL;
+    hlsl_block_add_instr(block, &vector_load->node);
+
+    if (idx->type == HLSL_IR_CONSTANT)
+    {
+        uint32_t swizzle = 0;
+
+        if (hlsl_ir_constant(idx)->value.u[0].u >= type->e.numeric.dimx)
+            return NULL;
+
+        hlsl_swizzle_set_component(&swizzle, 0, hlsl_ir_constant(idx)->value.u[0].u);
+        return hlsl_block_add_swizzle(ctx, block, swizzle, 1, &vector_load->node, &instr->loc);
+    }
+    else
     {
         struct hlsl_ir_node *eq, *swizzle, *c, *operands[HLSL_MAX_OPERANDS] = {0};
         unsigned int width = type->e.numeric.dimx;
         struct hlsl_constant_value value;
-        struct hlsl_ir_load *vector_load;
         enum hlsl_ir_expr_op op;
 
-        if (!(vector_load = hlsl_new_load_parent(ctx, deref, &instr->loc)))
-            return NULL;
-        hlsl_block_add_instr(block, &vector_load->node);
-
         swizzle = hlsl_block_add_swizzle(ctx, block, HLSL_SWIZZLE(X, X, X, X), width, idx, &instr->loc);
 
         value.u[0].u = 0;
@@ -4792,18 +5075,26 @@ static struct hlsl_ir_node *lower_nonconstant_array_loads(struct hlsl_ctx *ctx,
     return hlsl_block_add_simple_load(ctx, block, var, &instr->loc);
 }
 
-static struct hlsl_type *clone_texture_array_as_combined_sampler_array(struct hlsl_ctx *ctx, struct hlsl_type *type)
+static struct hlsl_type *clone_texture_array_as_combined_sampler_array(struct hlsl_ctx *ctx,
+        struct hlsl_type *type, const struct vkd3d_shader_location *loc)
 {
     struct hlsl_type *sampler_type;
 
     if (type->class == HLSL_CLASS_ARRAY)
     {
-        if (!(sampler_type = clone_texture_array_as_combined_sampler_array(ctx, type->e.array.type)))
+        if (!(sampler_type = clone_texture_array_as_combined_sampler_array(ctx, type->e.array.type, loc)))
             return NULL;
 
         return hlsl_new_array_type(ctx, sampler_type, type->e.array.elements_count, HLSL_ARRAY_GENERIC);
     }
 
+    if (type->sampler_dim > HLSL_SAMPLER_DIM_LAST_SAMPLER)
+    {
+        hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                "Cannot create a combined sampler from a %s.", debug_hlsl_type(ctx, type));
+        return NULL;
+    }
+
     return ctx->builtin_types.sampler[type->sampler_dim];
 }
 
@@ -4871,7 +5162,7 @@ static bool lower_separate_samples(struct hlsl_ctx *ctx, struct hlsl_ir_node *in
 
     if (!(var = hlsl_get_var(ctx->globals, name->buffer)))
     {
-        if (!(sampler_type = clone_texture_array_as_combined_sampler_array(ctx, resource->data_type)))
+        if (!(sampler_type = clone_texture_array_as_combined_sampler_array(ctx, resource->data_type, &instr->loc)))
         {
             hlsl_release_string_buffer(ctx, name);
             return false;
@@ -5325,44 +5616,6 @@ static struct hlsl_ir_node *lower_round(struct hlsl_ctx *ctx, struct hlsl_ir_nod
     return hlsl_block_add_binary_expr(ctx, block, HLSL_OP2_ADD, sum, neg);
 }
 
-/* Lower CEIL to FRC */
-static struct hlsl_ir_node *lower_ceil(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, struct hlsl_block *block)
-{
-    struct hlsl_ir_node *arg, *neg, *frc;
-    struct hlsl_ir_expr *expr;
-
-    if (instr->type != HLSL_IR_EXPR)
-        return NULL;
-
-    expr = hlsl_ir_expr(instr);
-    arg = expr->operands[0].node;
-    if (expr->op != HLSL_OP1_CEIL)
-        return NULL;
-
-    neg = hlsl_block_add_unary_expr(ctx, block, HLSL_OP1_NEG, arg, &instr->loc);
-    frc = hlsl_block_add_unary_expr(ctx, block, HLSL_OP1_FRACT, neg, &instr->loc);
-    return hlsl_block_add_binary_expr(ctx, block, HLSL_OP2_ADD, frc, arg);
-}
-
-/* Lower FLOOR to FRC */
-static struct hlsl_ir_node *lower_floor(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, struct hlsl_block *block)
-{
-    struct hlsl_ir_node *arg, *neg, *frc;
-    struct hlsl_ir_expr *expr;
-
-    if (instr->type != HLSL_IR_EXPR)
-        return NULL;
-
-    expr = hlsl_ir_expr(instr);
-    arg = expr->operands[0].node;
-    if (expr->op != HLSL_OP1_FLOOR)
-        return NULL;
-
-    frc = hlsl_block_add_unary_expr(ctx, block, HLSL_OP1_FRACT, arg, &instr->loc);
-    neg = hlsl_block_add_unary_expr(ctx, block, HLSL_OP1_NEG, frc, &instr->loc);
-    return hlsl_block_add_binary_expr(ctx, block, HLSL_OP2_ADD, neg, arg);
-}
-
 /* Lower SIN/COS to SINCOS for SM1.  */
 static struct hlsl_ir_node *lower_trig(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, struct hlsl_block *block)
 {
@@ -5478,45 +5731,6 @@ static struct hlsl_ir_node *lower_logic_not(struct hlsl_ctx *ctx, struct hlsl_ir
     return hlsl_block_add_expr(ctx, block, HLSL_OP1_REINTERPRET, operands, instr->data_type, &instr->loc);
 }
 
-/* Lower TERNARY to CMP for SM1. */
-static struct hlsl_ir_node *lower_ternary(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, struct hlsl_block *block)
-{
-    struct hlsl_ir_node *cond, *first, *second, *float_cond, *neg;
-    struct hlsl_ir_node *operands[HLSL_MAX_OPERANDS] = {0};
-    struct hlsl_ir_expr *expr;
-    struct hlsl_type *type;
-
-    if (instr->type != HLSL_IR_EXPR)
-        return NULL;
-
-    expr = hlsl_ir_expr(instr);
-    if (expr->op != HLSL_OP3_TERNARY)
-        return NULL;
-
-    cond = expr->operands[0].node;
-    first = expr->operands[1].node;
-    second = expr->operands[2].node;
-
-    if (cond->data_type->class > HLSL_CLASS_VECTOR || instr->data_type->class > HLSL_CLASS_VECTOR)
-    {
-        hlsl_fixme(ctx, &instr->loc, "Lower ternary of type other than scalar or vector.");
-        return NULL;
-    }
-
-    VKD3D_ASSERT(cond->data_type->e.numeric.type == HLSL_TYPE_BOOL);
-
-    type = hlsl_get_numeric_type(ctx, instr->data_type->class, HLSL_TYPE_FLOAT,
-            instr->data_type->e.numeric.dimx, instr->data_type->e.numeric.dimy);
-    float_cond = hlsl_block_add_cast(ctx, block, cond, type, &instr->loc);
-    neg = hlsl_block_add_unary_expr(ctx, block, HLSL_OP1_NEG, float_cond, &instr->loc);
-
-    memset(operands, 0, sizeof(operands));
-    operands[0] = neg;
-    operands[1] = second;
-    operands[2] = first;
-    return hlsl_block_add_expr(ctx, block, HLSL_OP3_CMP, operands, first->data_type, &instr->loc);
-}
-
 static struct hlsl_ir_node *lower_resource_load_bias(struct hlsl_ctx *ctx,
         struct hlsl_ir_node *instr, struct hlsl_block *block)
 {
@@ -5769,8 +5983,7 @@ struct hlsl_ir_node *hlsl_add_conditional(struct hlsl_ctx *ctx, struct hlsl_bloc
 
     if (cond_type->e.numeric.type != HLSL_TYPE_BOOL)
     {
-        cond_type = hlsl_get_numeric_type(ctx, cond_type->class, HLSL_TYPE_BOOL,
-                cond_type->e.numeric.dimx, cond_type->e.numeric.dimy);
+        cond_type = hlsl_change_base_type(ctx, cond_type, HLSL_TYPE_BOOL);
         condition = hlsl_block_add_cast(ctx, instrs, condition, cond_type, &condition->loc);
     }
 
@@ -5800,7 +6013,7 @@ static struct hlsl_ir_node *lower_int_division_sm4(struct hlsl_ctx *ctx,
         return NULL;
     if (type->e.numeric.type != HLSL_TYPE_INT)
         return NULL;
-    utype = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_UINT, type->e.numeric.dimx, type->e.numeric.dimy);
+    utype = hlsl_change_base_type(ctx, type, HLSL_TYPE_UINT);
 
     xor = hlsl_block_add_binary_expr(ctx, block, HLSL_OP2_BIT_XOR, arg1, arg2);
 
@@ -5838,7 +6051,7 @@ static struct hlsl_ir_node *lower_int_modulus_sm4(struct hlsl_ctx *ctx,
         return NULL;
     if (type->e.numeric.type != HLSL_TYPE_INT)
         return NULL;
-    utype = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_UINT, type->e.numeric.dimx, type->e.numeric.dimy);
+    utype = hlsl_change_base_type(ctx, type, HLSL_TYPE_UINT);
 
     for (i = 0; i < type->e.numeric.dimx; ++i)
         high_bit_value.u[i].u = 0x80000000;
@@ -5938,7 +6151,7 @@ static struct hlsl_ir_node *lower_float_modulus(struct hlsl_ctx *ctx,
         return NULL;
     if (type->e.numeric.type != HLSL_TYPE_FLOAT)
         return NULL;
-    btype = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_BOOL, type->e.numeric.dimx, type->e.numeric.dimy);
+    btype = hlsl_change_base_type(ctx, type, HLSL_TYPE_BOOL);
 
     mul1 = hlsl_block_add_binary_expr(ctx, block, HLSL_OP2_MUL, arg2, arg1);
     neg1 = hlsl_block_add_unary_expr(ctx, block, HLSL_OP1_NEG, mul1, &instr->loc);
@@ -5981,8 +6194,7 @@ static bool lower_discard_neg(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr,
 
     operands[0] = jump->condition.node;
     operands[1] = zero;
-    cmp_type = hlsl_get_numeric_type(ctx, arg_type->class, HLSL_TYPE_BOOL,
-            arg_type->e.numeric.dimx, arg_type->e.numeric.dimy);
+    cmp_type = hlsl_change_base_type(ctx, arg_type, HLSL_TYPE_BOOL);
     cmp = hlsl_block_add_expr(ctx, &block, HLSL_OP2_LESS, operands, cmp_type, &instr->loc);
 
     bool_false = hlsl_block_add_constant(ctx, &block,
@@ -6368,8 +6580,9 @@ static void compute_liveness_recurse(struct hlsl_block *block, unsigned int loop
             var = interlocked->dst.var;
             var->last_read = max(var->last_read, last_read);
             deref_mark_last_read(&interlocked->dst, last_read);
-            interlocked->coords.node->last_read = last_read;
             interlocked->value.node->last_read = last_read;
+            if (interlocked->coords.node)
+                interlocked->coords.node->last_read = last_read;
             if (interlocked->cmp_value.node)
                 interlocked->cmp_value.node->last_read = last_read;
             break;
@@ -6453,12 +6666,18 @@ struct register_allocator
         /* If an allocation is VIP, no new allocations can be made in the
          * register unless they are VIP as well. */
         bool vip;
+        /* Clip/Cull allocations can't share registers. */
+        bool clip_cull;
     } *allocations;
     size_t count, capacity;
 
     /* Total number of registers allocated so far. */
     uint32_t reg_count;
 
+    /* Total number of registers used for Clip or Cull allocations.
+     * Used to limit Clip/Cull allocations to two registers. */
+    uint32_t clip_cull_count;
+
     /* Special flag so allocations that can share registers prioritize those
      * that will result in smaller writemasks.
      * For instance, a single-register allocation would prefer to share a register
@@ -6468,7 +6687,7 @@ struct register_allocator
 };
 
 static unsigned int get_available_writemask(const struct register_allocator *allocator,
-        uint32_t reg_idx, int mode, bool vip)
+        uint32_t reg_idx, int mode, bool vip, bool clip_cull)
 {
     unsigned int writemask = VKD3DSP_WRITEMASK_ALL;
     size_t i;
@@ -6484,6 +6703,8 @@ static unsigned int get_available_writemask(const struct register_allocator *all
                 writemask = 0;
             if (allocation->vip && !vip)
                 writemask = 0;
+            if (allocation->clip_cull != clip_cull)
+                writemask = 0;
         }
 
         if (!writemask)
@@ -6493,8 +6714,8 @@ static unsigned int get_available_writemask(const struct register_allocator *all
     return writemask;
 }
 
-static void record_allocation(struct hlsl_ctx *ctx, struct register_allocator *allocator, uint32_t reg_idx,
-        unsigned int writemask, int mode, bool vip)
+static void record_allocation(struct hlsl_ctx *ctx, struct register_allocator *allocator,
+        uint32_t reg_idx, unsigned int writemask, int mode, bool vip, bool clip_cull)
 {
     struct allocation *allocation;
 
@@ -6507,6 +6728,7 @@ static void record_allocation(struct hlsl_ctx *ctx, struct register_allocator *a
     allocation->writemask = writemask;
     allocation->mode = mode;
     allocation->vip = vip;
+    allocation->clip_cull = clip_cull;
 
     allocator->reg_count = max(allocator->reg_count, reg_idx + 1);
 }
@@ -6523,7 +6745,7 @@ static void record_allocation(struct hlsl_ctx *ctx, struct register_allocator *a
  * 'vip' can be used so that no new allocations can be made in the given register
  *      unless they are 'vip' as well. */
 static struct hlsl_reg allocate_register(struct hlsl_ctx *ctx, struct register_allocator *allocator,
-        unsigned int reg_size, unsigned int component_count, int mode, bool force_align, bool vip)
+        unsigned int reg_size, unsigned int component_count, int mode, bool force_align, bool vip, bool clip_cull)
 {
     struct hlsl_reg ret = {.allocation_size = 1, .allocated = true};
     unsigned int required_size = force_align ? 4 : reg_size;
@@ -6536,7 +6758,7 @@ static struct hlsl_reg allocate_register(struct hlsl_ctx *ctx, struct register_a
     {
         for (uint32_t reg_idx = 0; reg_idx < allocator->reg_count; ++reg_idx)
         {
-            unsigned int available_writemask = get_available_writemask(allocator, reg_idx, mode, vip);
+            unsigned int available_writemask = get_available_writemask(allocator, reg_idx, mode, vip, clip_cull);
 
             if (vkd3d_popcount(available_writemask) >= pref)
             {
@@ -6548,7 +6770,7 @@ static struct hlsl_reg allocate_register(struct hlsl_ctx *ctx, struct register_a
                 ret.writemask = hlsl_combine_writemasks(writemask,
                         vkd3d_write_mask_from_component_count(component_count));
 
-                record_allocation(ctx, allocator, reg_idx, writemask, mode, vip);
+                record_allocation(ctx, allocator, reg_idx, writemask, mode, vip, clip_cull);
                 return ret;
             }
         }
@@ -6558,7 +6780,8 @@ static struct hlsl_reg allocate_register(struct hlsl_ctx *ctx, struct register_a
     ret.id = allocator->reg_count;
     ret.writemask = vkd3d_write_mask_from_component_count(component_count);
     record_allocation(ctx, allocator, allocator->reg_count,
-            vkd3d_write_mask_from_component_count(reg_size), mode, vip);
+            vkd3d_write_mask_from_component_count(reg_size), mode, vip, clip_cull);
+    allocator->clip_cull_count += clip_cull;
     return ret;
 }
 
@@ -6571,11 +6794,11 @@ static bool is_range_available(const struct register_allocator *allocator,
 
     for (i = 0; i < (reg_size / 4); ++i)
     {
-        writemask = get_available_writemask(allocator, reg_idx + i, mode, vip);
+        writemask = get_available_writemask(allocator, reg_idx + i, mode, vip, false);
         if (writemask != VKD3DSP_WRITEMASK_ALL)
             return false;
     }
-    writemask = get_available_writemask(allocator, reg_idx + (reg_size / 4), mode, vip);
+    writemask = get_available_writemask(allocator, reg_idx + (reg_size / 4), mode, vip, false);
     if ((writemask & last_reg_mask) != last_reg_mask)
         return false;
     return true;
@@ -6595,9 +6818,9 @@ static struct hlsl_reg allocate_range(struct hlsl_ctx *ctx,
     }
 
     for (i = 0; i < reg_size / 4; ++i)
-        record_allocation(ctx, allocator, reg_idx + i, VKD3DSP_WRITEMASK_ALL, mode, vip);
+        record_allocation(ctx, allocator, reg_idx + i, VKD3DSP_WRITEMASK_ALL, mode, vip, false);
     if (reg_size % 4)
-        record_allocation(ctx, allocator, reg_idx + (reg_size / 4), (1u << (reg_size % 4)) - 1, mode, vip);
+        record_allocation(ctx, allocator, reg_idx + (reg_size / 4), (1u << (reg_size % 4)) - 1, mode, vip, false);
 
     ret.type = allocator->type;
     ret.id = reg_idx;
@@ -6614,7 +6837,7 @@ static struct hlsl_reg allocate_numeric_registers_for_type(struct hlsl_ctx *ctx,
     /* FIXME: We could potentially pack structs or arrays more efficiently... */
 
     if (type->class <= HLSL_CLASS_VECTOR)
-        return allocate_register(ctx, allocator, type->e.numeric.dimx, type->e.numeric.dimx, 0, false, false);
+        return allocate_register(ctx, allocator, type->e.numeric.dimx, type->e.numeric.dimx, 0, false, false, false);
     else
         return allocate_range(ctx, allocator, reg_size, 0, false);
 }
@@ -6993,13 +7216,17 @@ static bool find_constant(struct hlsl_ctx *ctx, const float *f, unsigned int cou
     for (size_t i = 0; i < defs->count; ++i)
     {
         const struct hlsl_constant_register *reg = &defs->regs[i];
+        const float *regf;
 
+        if (reg->is_int)
+            continue;
+        regf = (float[]){reg->value[0].f, reg->value[1].f, reg->value[2].f, reg->value[3].f};
         for (size_t j = 0; j <= 4 - count; ++j)
         {
             unsigned int writemask = ((1u << count) - 1) << j;
 
             if ((reg->allocated_mask & writemask) == writemask
-                    && !memcmp(f, &reg->value.f[j], count * sizeof(float)))
+                    && !memcmp(f, &regf[j], count * sizeof(float)))
             {
                 ret->type = VKD3DSPR_CONST;
                 ret->id = reg->index;
@@ -7014,7 +7241,8 @@ static bool find_constant(struct hlsl_ctx *ctx, const float *f, unsigned int cou
     return false;
 }
 
-static void record_constant(struct hlsl_ctx *ctx, unsigned int component_index, float f,
+static void record_constant_value(struct hlsl_ctx *ctx, unsigned int component_index,
+        bool is_int, union hlsl_constant_value_component v,
         const struct vkd3d_shader_location *loc)
 {
     struct hlsl_constant_defs *defs = &ctx->constant_defs;
@@ -7024,9 +7252,9 @@ static void record_constant(struct hlsl_ctx *ctx, unsigned int component_index,
     for (i = 0; i < defs->count; ++i)
     {
         reg = &defs->regs[i];
-        if (reg->index == (component_index / 4))
+        if (reg->is_int == is_int && reg->index == (component_index / 4))
         {
-            reg->value.f[component_index % 4] = f;
+            reg->value[component_index % 4] = v;
             reg->allocated_mask |= (1u << (component_index % 4));
             return;
         }
@@ -7036,12 +7264,31 @@ static void record_constant(struct hlsl_ctx *ctx, unsigned int component_index,
         return;
     reg = &defs->regs[defs->count++];
     memset(reg, 0, sizeof(*reg));
+    reg->is_int = is_int;
     reg->index = component_index / 4;
-    reg->value.f[component_index % 4] = f;
+    reg->value[component_index % 4] = v;
     reg->allocated_mask = (1u << (component_index % 4));
     reg->loc = *loc;
 }
 
+static void record_constant(struct hlsl_ctx *ctx, unsigned int component_index,
+        float f, const struct vkd3d_shader_location *loc)
+{
+    union hlsl_constant_value_component v;
+
+    v.f = f;
+    record_constant_value(ctx, component_index, false, v, loc);
+}
+
+static void record_constint(struct hlsl_ctx *ctx, unsigned int component_index,
+        int i, const struct vkd3d_shader_location *loc)
+{
+    union hlsl_constant_value_component v;
+
+    v.i = i;
+    record_constant_value(ctx, component_index, true, v, loc);
+}
+
 static void allocate_const_registers_recurse(struct hlsl_ctx *ctx,
         struct hlsl_block *block, struct register_allocator *allocator)
 {
@@ -7178,6 +7425,34 @@ static void sort_uniforms_by_bind_count(struct hlsl_ctx *ctx, enum hlsl_regset r
     list_move_tail(&ctx->extern_vars, &sorted);
 }
 
+static bool allocate_constint_registers_for_loops(struct hlsl_ctx *ctx,
+        struct hlsl_ir_node *instr, void *context)
+{
+    struct register_allocator *allocator = (struct register_allocator *)context;
+    struct hlsl_ir_loop *loop;
+
+    if (ctx->d3d255intconst.allocated)
+        return false;
+
+    if (instr->type != HLSL_IR_LOOP)
+        return false;
+    loop = hlsl_ir_loop(instr);
+
+    if (!loop->limiter)
+    {
+        ctx->d3d255intconst = allocate_register(ctx, allocator, 4, 4, 0, false, false, false);
+        TRACE("Allocated D3D255INTCONST to %s.\n",
+                debug_register(ctx->d3d255intconst, hlsl_get_vector_type(ctx, HLSL_TYPE_INT, 4)));
+        record_constint(ctx, ctx->d3d255intconst.id * 4 + 0, 255, &instr->loc);
+        record_constint(ctx, ctx->d3d255intconst.id * 4 + 1, 0, &instr->loc);
+        record_constint(ctx, ctx->d3d255intconst.id * 4 + 2, 0, &instr->loc);
+        record_constint(ctx, ctx->d3d255intconst.id * 4 + 3, 0, &instr->loc);
+        return true;
+    }
+
+    return false;
+}
+
 /* In SM2, 'sincos' expects specific constants as src1 and src2 arguments.
  * These have to be referenced directly, i.e. as 'c' not 'r'. */
 static void allocate_sincos_const_registers(struct hlsl_ctx *ctx, struct hlsl_block *block,
@@ -7218,6 +7493,7 @@ static void allocate_sincos_const_registers(struct hlsl_ctx *ctx, struct hlsl_bl
 static void allocate_const_registers(struct hlsl_ctx *ctx, struct hlsl_block *body)
 {
     struct register_allocator allocator = {.type = VKD3DSPR_CONST}, allocator_used = {.type = VKD3DSPR_CONST};
+    struct register_allocator allocator_constint = {.type = VKD3DSPR_CONSTINT};
     struct hlsl_ir_var *var;
 
     sort_uniforms_by_bind_count(ctx, HLSL_REGSET_NUMERIC);
@@ -7240,14 +7516,14 @@ static void allocate_const_registers(struct hlsl_ctx *ctx, struct hlsl_block *bo
             {
                 if (i < bind_count)
                 {
-                    if (get_available_writemask(&allocator_used, reg_idx + i, 0, false) != VKD3DSP_WRITEMASK_ALL)
+                    if (get_available_writemask(&allocator_used, reg_idx + i, 0, false, false) != VKD3DSP_WRITEMASK_ALL)
                     {
                         hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
                                 "Overlapping register() reservations on 'c%u'.", reg_idx + i);
                     }
-                    record_allocation(ctx, &allocator_used, reg_idx + i, VKD3DSP_WRITEMASK_ALL, 0, false);
+                    record_allocation(ctx, &allocator_used, reg_idx + i, VKD3DSP_WRITEMASK_ALL, 0, false, false);
                 }
-                record_allocation(ctx, &allocator, reg_idx + i, VKD3DSP_WRITEMASK_ALL, 0, false);
+                record_allocation(ctx, &allocator, reg_idx + i, VKD3DSP_WRITEMASK_ALL, 0, false, false);
             }
 
             var->regs[HLSL_REGSET_NUMERIC].type = VKD3DSPR_CONST;
@@ -7281,6 +7557,9 @@ static void allocate_const_registers(struct hlsl_ctx *ctx, struct hlsl_block *bo
 
     allocate_sincos_const_registers(ctx, body, &allocator);
 
+    hlsl_transform_ir(ctx, allocate_constint_registers_for_loops, body, &allocator_constint);
+
+    vkd3d_free(allocator_constint.allocations);
     vkd3d_free(allocator.allocations);
 }
 
@@ -7378,6 +7657,7 @@ static void allocate_semantic_register(struct hlsl_ctx *ctx, struct hlsl_ir_var
     struct vkd3d_shader_version version;
     bool special_interpolation = false;
     bool vip_allocation = false;
+    bool clip_cull = false;
     uint32_t reg;
     bool builtin;
 
@@ -7415,7 +7695,7 @@ static void allocate_semantic_register(struct hlsl_ctx *ctx, struct hlsl_ir_var
         enum vkd3d_shader_sysval_semantic semantic;
         bool has_idx;
 
-        if (!sm4_sysval_semantic_from_semantic_name(&semantic, &version, ctx->semantic_compat_mapping, ctx->domain,
+        if (!sm4_sysval_semantic_from_semantic_name(&semantic, &version, ctx->compatibility_flags, ctx->domain,
                 var->semantic.name, var->semantic.index, output, ctx->is_patch_constant_func, is_primitive))
         {
             hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC,
@@ -7437,6 +7717,8 @@ static void allocate_semantic_register(struct hlsl_ctx *ctx, struct hlsl_ir_var
                 || semantic == VKD3D_SHADER_SV_VIEWPORT_ARRAY_INDEX
                 || semantic == VKD3D_SHADER_SV_PRIMITIVE_ID)
             vip_allocation = true;
+        else if (vsir_sysval_semantic_is_clip_cull(semantic))
+            clip_cull = true;
 
         if (semantic == VKD3D_SHADER_SV_IS_FRONT_FACE || semantic == VKD3D_SHADER_SV_SAMPLE_INDEX
                 || (version.type == VKD3D_SHADER_TYPE_DOMAIN && !output && !is_primitive)
@@ -7459,8 +7741,16 @@ static void allocate_semantic_register(struct hlsl_ctx *ctx, struct hlsl_ir_var
         if (version.major >= 4 && !special_interpolation)
             mode = get_interpolation_mode(&version, var->data_type, var->storage_modifiers);
 
-        var->regs[HLSL_REGSET_NUMERIC] = allocate_register(ctx, allocator,
-                reg_size, component_count, mode, var->force_align, vip_allocation);
+        var->regs[HLSL_REGSET_NUMERIC] = allocate_register(ctx, allocator, reg_size,
+                component_count, mode, var->force_align, vip_allocation, clip_cull);
+        var->regs[HLSL_REGSET_NUMERIC].type = output ? VKD3DSPR_OUTPUT : VKD3DSPR_INPUT;
+
+        if (clip_cull && allocator->clip_cull_count > 2)
+        {
+            hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC,
+                "Too many SV_ClipDistance or SV_CullDistance parameters.");
+            return;
+        }
 
         TRACE("Allocated %s to %s (mode %d).\n", var->name,
                 debug_register(var->regs[HLSL_REGSET_NUMERIC], var->data_type), mode);
@@ -7709,6 +7999,10 @@ static void allocate_buffers(struct hlsl_ctx *ctx)
             var->buffer = ctx->params_buffer;
     }
 
+    LIST_FOR_EACH_ENTRY(buffer, &ctx->buffers, struct hlsl_buffer, entry)
+    {
+        buffer->used_size = buffer->size = 0;
+    }
     hlsl_calculate_buffer_offsets(ctx);
     validate_buffer_offsets(ctx);
 
@@ -9115,7 +9409,7 @@ static void generate_vsir_signature_entry(struct hlsl_ctx *ctx, struct vsir_prog
         enum hlsl_base_type numeric_type;
         bool has_idx, ret;
 
-        ret = sm4_sysval_semantic_from_semantic_name(&sysval, &program->shader_version, ctx->semantic_compat_mapping,
+        ret = sm4_sysval_semantic_from_semantic_name(&sysval, &program->shader_version, ctx->compatibility_flags,
                 ctx->domain, var->semantic.name, var->semantic.index, output, ctx->is_patch_constant_func, is_primitive);
         VKD3D_ASSERT(ret);
         if (sysval == ~0u)
@@ -9165,12 +9459,32 @@ static void generate_vsir_signature_entry(struct hlsl_ctx *ctx, struct vsir_prog
                 break;
         }
 
-        if (sysval == VKD3D_SHADER_SV_TARGET && !ascii_strcasecmp(name, "color"))
+        if (sysval == VKD3D_SHADER_SV_STENCIL_REF)
+        {
+            if (hlsl_version_lt(ctx, 5, 0))
+                hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INCOMPATIBLE_PROFILE,
+                        "Stencil export is only supported in shader model 5.0 or higher.");
+            if (var->semantic.index)
+                hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC,
+                        "Invalid semantic index %u for semantic variable %s.", var->semantic.index, var->name);
+            if (!hlsl_is_vec1(var->data_type) || numeric_type != HLSL_TYPE_UINT)
+            {
+                if ((string = hlsl_type_to_string(ctx, var->data_type)))
+                    hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                            "Invalid data type %s for semantic variable %s.", string->buffer, var->name);
+                hlsl_release_string_buffer(ctx, string);
+            }
+            program->global_flags |= VKD3DSGF_ENABLE_STENCIL_REF;
+        }
+
+        if (sysval == VKD3D_SHADER_SV_TARGET && ascii_strcasecmp(name, "SV_Target"))
             name = "SV_Target";
-        else if (sysval == VKD3D_SHADER_SV_DEPTH && !ascii_strcasecmp(name, "depth"))
-            name ="SV_Depth";
-        else if (sysval == VKD3D_SHADER_SV_POSITION && !ascii_strcasecmp(name, "position"))
+        else if (sysval == VKD3D_SHADER_SV_DEPTH && ascii_strcasecmp(name, "SV_Depth"))
+            name = "SV_Depth";
+        else if (sysval == VKD3D_SHADER_SV_POSITION && ascii_strcasecmp(name, "SV_Position"))
             name = "SV_Position";
+        else if (sysval == VKD3D_SHADER_SV_IS_FRONT_FACE && ascii_strcasecmp(name, "SV_IsFrontFace"))
+            name = "SV_IsFrontFace";
     }
     else
     {
@@ -9238,6 +9552,10 @@ static void generate_vsir_signature_entry(struct hlsl_ctx *ctx, struct vsir_prog
         component_type = VKD3D_SHADER_COMPONENT_FLOAT;
     }
 
+    if (sysval == VKD3D_SHADER_SV_IS_FRONT_FACE && var->data_type->e.numeric.dimx > 1)
+        hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC,
+                "%s input must have only 1 component.", var->semantic.name);
+
     if (!vkd3d_array_reserve((void **)&signature->elements, &signature->elements_capacity,
             signature->element_count + 1, sizeof(*signature->elements)))
     {
@@ -9385,27 +9703,44 @@ static void sm1_generate_vsir_constant_defs(struct hlsl_ctx *ctx,
             ctx->result = VKD3D_ERROR_OUT_OF_MEMORY;
             return;
         }
-        if (!vsir_instruction_init_with_params(program, ins, &constant_reg->loc, VSIR_OP_DEF, 1, 1))
+        if (!vsir_instruction_init_with_params(program, ins, &constant_reg->loc,
+                constant_reg->is_int ? VSIR_OP_DEFI : VSIR_OP_DEF, 1, 1))
         {
             vsir_instruction_init(ins, &constant_reg->loc, VSIR_OP_NOP);
             ctx->result = VKD3D_ERROR_OUT_OF_MEMORY;
             return;
         }
 
-        dst = &ins->dst[0];
-        vsir_dst_operand_init(dst, VKD3DSPR_CONST, VSIR_DATA_F32, 1);
-        dst->reg.dimension = VSIR_DIMENSION_VEC4;
-        dst->reg.idx[0].offset = constant_reg->index;
-        dst->write_mask = VKD3DSP_WRITEMASK_ALL;
+        if (constant_reg->is_int)
+        {
+            dst = &ins->dst[0];
+            vsir_operand_init(&dst->reg, VKD3DSPR_CONSTINT, VSIR_DATA_I32, 1);
+            dst->reg.dimension = VSIR_DIMENSION_VEC4;
+            dst->reg.idx[0].offset = constant_reg->index;
+            dst->write_mask = VKD3DSP_WRITEMASK_ALL;
 
-        src = &ins->src[0];
-        vsir_src_operand_init(src, VKD3DSPR_IMMCONST, VSIR_DATA_F32, 0);
-        src->reg.dimension = VSIR_DIMENSION_VEC4;
-        for (x = 0; x < 4; ++x)
+            src = &ins->src[0];
+            vsir_src_operand_init(src, VKD3DSPR_IMMCONST, VSIR_DATA_I32, 0);
+            src->reg.dimension = VSIR_DIMENSION_VEC4;
+            for (x = 0; x < 4; ++x)
+                src->reg.u.immconst_u32[x] = constant_reg->value[x].u;
+            src->swizzle = VKD3D_SHADER_NO_SWIZZLE;
+        }
+        else
         {
-            src->reg.u.immconst_f32[x] = constant_reg->value.f[x];
+            dst = &ins->dst[0];
+            vsir_operand_init(&dst->reg, VKD3DSPR_CONST, VSIR_DATA_F32, 1);
+            dst->reg.dimension = VSIR_DIMENSION_VEC4;
+            dst->reg.idx[0].offset = constant_reg->index;
+            dst->write_mask = VKD3DSP_WRITEMASK_ALL;
+
+            src = &ins->src[0];
+            vsir_src_operand_init(src, VKD3DSPR_IMMCONST, VSIR_DATA_F32, 0);
+            src->reg.dimension = VSIR_DIMENSION_VEC4;
+            for (x = 0; x < 4; ++x)
+                src->reg.u.immconst_f32[x] = constant_reg->value[x].f;
+            src->swizzle = VKD3D_SHADER_NO_SWIZZLE;
         }
-        src->swizzle = VKD3D_SHADER_NO_SWIZZLE;
     }
 }
 
@@ -9413,10 +9748,10 @@ static void sm1_generate_vsir_sampler_dcls(struct hlsl_ctx *ctx,
         struct vsir_program *program, struct hlsl_block *block)
 {
     enum vkd3d_shader_resource_type resource_type;
-    struct vkd3d_shader_register_range *range;
     struct vkd3d_shader_semantic *semantic;
     struct vkd3d_shader_instruction *ins;
     enum hlsl_sampler_dim sampler_dim;
+    struct vsir_register_range *range;
     struct vsir_dst_operand *dst;
     struct hlsl_ir_var *var;
     unsigned int i, count;
@@ -9469,7 +9804,7 @@ static void sm1_generate_vsir_sampler_dcls(struct hlsl_ctx *ctx,
                 semantic->resource_type = resource_type;
 
                 dst = &semantic->resource.reg;
-                vsir_register_init(&dst->reg, VKD3DSPR_COMBINED_SAMPLER, VSIR_DATA_F32, 1);
+                vsir_operand_init(&dst->reg, VKD3DSPR_COMBINED_SAMPLER, VSIR_DATA_F32, 1);
                 dst->reg.dimension = VSIR_DIMENSION_NONE;
                 dst->reg.idx[0].offset = var->regs[HLSL_REGSET_SAMPLERS].index + i;
                 dst->write_mask = 0;
@@ -9574,7 +9909,7 @@ static void vsir_src_from_hlsl_node(struct vsir_src_operand *src, struct hlsl_ct
     }
     else
     {
-        vsir_register_init(&src->reg, instr->reg.type, vsir_data_type_from_hlsl_instruction(ctx, instr), 1);
+        vsir_operand_init(&src->reg, instr->reg.type, vsir_data_type_from_hlsl_instruction(ctx, instr), 1);
         src->reg.idx[0].offset = instr->reg.id;
         src->reg.dimension = VSIR_DIMENSION_VEC4;
         src->swizzle = generate_vsir_get_src_swizzle(instr->reg.writemask, map_writemask);
@@ -9598,7 +9933,7 @@ static struct vsir_src_operand *sm4_generate_vsir_new_idx_src(struct hlsl_ctx *c
 }
 
 static bool sm4_generate_vsir_numeric_reg_from_deref(struct hlsl_ctx *ctx, struct vsir_program *program,
-        struct vkd3d_shader_register *reg, uint32_t *writemask, const struct hlsl_deref *deref)
+        struct vsir_operand *reg, uint32_t *writemask, const struct hlsl_deref *deref)
 {
     const struct hlsl_ir_var *var = deref->var;
     unsigned int offset_const_deref;
@@ -9635,7 +9970,7 @@ static bool sm4_generate_vsir_numeric_reg_from_deref(struct hlsl_ctx *ctx, struc
 }
 
 static bool sm4_generate_vsir_reg_from_deref(struct hlsl_ctx *ctx, struct vsir_program *program,
-        struct vkd3d_shader_register *reg, uint32_t *writemask, const struct hlsl_deref *deref)
+        struct vsir_operand *reg, uint32_t *writemask, const struct hlsl_deref *deref)
 {
     const struct vkd3d_shader_version *version = &program->shader_version;
     const struct hlsl_type *data_type = hlsl_deref_get_type(ctx, deref);
@@ -10030,7 +10365,7 @@ static bool sm1_generate_vsir_instr_expr_cast(struct hlsl_ctx *ctx,
                     return true;
 
                 case HLSL_TYPE_DOUBLE:
-                    if (ctx->double_as_float_alias)
+                    if (ctx->compatibility_flags & VKD3D_SHADER_COMPILE_OPTION_DOUBLE_AS_FLOAT_ALIAS)
                     {
                         generate_vsir_instr_expr_single_instr_op(ctx, program, expr, VSIR_OP_MOV, 0, 0, true);
                         return true;
@@ -10095,11 +10430,13 @@ static bool sm1_generate_vsir_instr_expr(struct hlsl_ctx *ctx, struct vsir_progr
 {
     struct hlsl_ir_node *instr = &expr->node;
     struct hlsl_type *type = instr->data_type;
+    struct vkd3d_shader_instruction *ins;
 
     if (!hlsl_is_numeric_type(type))
         goto err;
 
-    if (type->e.numeric.type == HLSL_TYPE_DOUBLE && !ctx->double_as_float_alias)
+    if (type->e.numeric.type == HLSL_TYPE_DOUBLE
+            && !(ctx->compatibility_flags & VKD3D_SHADER_COMPILE_OPTION_DOUBLE_AS_FLOAT_ALIAS))
     {
         hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
                 "The 'double' type is not supported for the %s profile.", ctx->profile->name);
@@ -10115,6 +10452,38 @@ static bool sm1_generate_vsir_instr_expr(struct hlsl_ctx *ctx, struct vsir_progr
         case HLSL_OP1_CAST:
             return sm1_generate_vsir_instr_expr_cast(ctx, program, expr);
 
+        case HLSL_OP1_CEIL:
+            if (!hlsl_type_is_floating_point(type))
+                goto err;
+            if (program->shader_version.type == VKD3D_SHADER_TYPE_VERTEX || hlsl_version_ge(ctx, 2, 0))
+            {
+                uint32_t ssa_frc = ctx->ssa_count++;
+
+                /* FIXME: Native frc in vs 1.x has some weird constraints we're not respecting. */
+
+                /* frc sr0, -SRC
+                 * add DST, SRC, sr0
+                 */
+
+                if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_FRC, 1, 1)))
+                    return false;
+                vsir_dst_operand_init_ssa_f32v4(&ins->dst[0], ssa_frc);
+                vsir_src_from_hlsl_node(&ins->src[0], ctx, expr->operands[0].node, ins->dst[0].write_mask);
+                ins->src[0].modifiers = VKD3DSPSM_NEG;
+
+                if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_ADD, 1, 2)))
+                    return false;
+                vsir_dst_from_hlsl_node(&ins->dst[0], ctx, instr);
+                vsir_src_from_hlsl_node(&ins->src[0], ctx, expr->operands[0].node, ins->dst[0].write_mask);
+                vsir_src_operand_init_ssa_f32v4(&ins->src[1], ssa_frc);
+            }
+            else
+            {
+                /* Not supported in native ps 1.x. */
+                goto err;
+            }
+            break;
+
         case HLSL_OP1_COS_REDUCED:
             VKD3D_ASSERT(expr->node.reg.writemask == VKD3DSP_WRITEMASK_0);
             if (!hlsl_type_is_floating_point(type))
@@ -10140,6 +10509,38 @@ static bool sm1_generate_vsir_instr_expr(struct hlsl_ctx *ctx, struct vsir_progr
             sm1_generate_vsir_instr_expr_per_component_instr_op(ctx, program, expr, VSIR_OP_EXP);
             break;
 
+        case HLSL_OP1_FLOOR:
+            if (!hlsl_type_is_floating_point(type))
+                goto err;
+            if (program->shader_version.type == VKD3D_SHADER_TYPE_VERTEX || hlsl_version_ge(ctx, 2, 0))
+            {
+                uint32_t ssa_frc = ctx->ssa_count++;
+
+                /* FIXME: Native frc in vs 1.x has some weird constraints we're not respecting. */
+
+                /* frc sr0, SRC
+                 * add DST, SRC, -sr0
+                 */
+
+                if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_FRC, 1, 1)))
+                    return false;
+                vsir_dst_operand_init_ssa_f32v4(&ins->dst[0], ssa_frc);
+                vsir_src_from_hlsl_node(&ins->src[0], ctx, expr->operands[0].node, ins->dst[0].write_mask);
+
+                if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_ADD, 1, 2)))
+                    return false;
+                vsir_dst_from_hlsl_node(&ins->dst[0], ctx, instr);
+                vsir_src_from_hlsl_node(&ins->src[0], ctx, expr->operands[0].node, ins->dst[0].write_mask);
+                vsir_src_operand_init_ssa_f32v4(&ins->src[1], ssa_frc);
+                ins->src[1].modifiers = VKD3DSPSM_NEG;
+            }
+            else
+            {
+                /* Not supported in native ps 1.x. */
+                goto err;
+            }
+            break;
+
         case HLSL_OP1_LOG2:
             if (!hlsl_type_is_floating_point(type))
                 goto err;
@@ -10256,6 +10657,53 @@ static bool sm1_generate_vsir_instr_expr(struct hlsl_ctx *ctx, struct vsir_progr
             generate_vsir_instr_expr_single_instr_op(ctx, program, expr, VSIR_OP_MAD, 0, 0, true);
             break;
 
+        case HLSL_OP3_TERNARY:
+            VKD3D_ASSERT(expr->operands[0].node->data_type->e.numeric.type == HLSL_TYPE_BOOL);
+            /* bool in sm1 is 1.0 (true) or 0.0 (false). */
+            if (program->shader_version.type == VKD3D_SHADER_TYPE_VERTEX)
+            {
+                uint32_t ssa_add = ctx->ssa_count++;
+
+                /* add sr0, SRC1, -SRC2
+                 * mad DST, SRC0, sr0, SRC2
+                 *
+                 * That is, essentially lerp between the two. This is not
+                 * IEEE 754 safe, but then again neither is sm1 in general,
+                 * and this is what native emits. */
+
+                if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_ADD, 1, 2)))
+                    return false;
+                vsir_dst_operand_init_ssa_f32v4(&ins->dst[0], ssa_add);
+                vsir_src_from_hlsl_node(&ins->src[0], ctx, expr->operands[1].node, ins->dst[0].write_mask);
+                vsir_src_from_hlsl_node(&ins->src[1], ctx, expr->operands[2].node, ins->dst[0].write_mask);
+                ins->src[1].modifiers = VKD3DSPSM_NEG;
+
+                if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_MAD, 1, 3)))
+                    return false;
+                vsir_dst_from_hlsl_node(&ins->dst[0], ctx, instr);
+                vsir_src_from_hlsl_node(&ins->src[0], ctx, expr->operands[0].node, ins->dst[0].write_mask);
+                vsir_src_operand_init_ssa_f32v4(&ins->src[1], ssa_add);
+                vsir_src_from_hlsl_node(&ins->src[2], ctx, expr->operands[2].node, ins->dst[0].write_mask);
+            }
+            else if (hlsl_version_ge(ctx, 2, 0))
+            {
+                /* cmp DST, -SRC0, SRC2, SRC1 */
+
+                if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_CMP, 1, 3)))
+                    return false;
+                vsir_dst_from_hlsl_node(&ins->dst[0], ctx, instr);
+                vsir_src_from_hlsl_node(&ins->src[0], ctx, expr->operands[0].node, ins->dst[0].write_mask);
+                ins->src[0].modifiers = VKD3DSPSM_NEG;
+                vsir_src_from_hlsl_node(&ins->src[1], ctx, expr->operands[2].node, ins->dst[0].write_mask);
+                vsir_src_from_hlsl_node(&ins->src[2], ctx, expr->operands[1].node, ins->dst[0].write_mask);
+            }
+            else
+            {
+                /* FIXME: We can use CMP in 1.2-1.4 but we can't use VKD3DSPSM_NEG. */
+                goto err;
+            }
+            break;
+
         default:
             goto err;
     }
@@ -10314,12 +10762,12 @@ static void sm1_generate_vsir_init_dst_operand_from_deref(struct hlsl_ctx *ctx,
 
     if (type == VKD3DSPR_DEPTHOUT)
     {
-        vsir_register_init(&dst->reg, type, VSIR_DATA_F32, 0);
+        vsir_operand_init(&dst->reg, type, VSIR_DATA_F32, 0);
         dst->reg.dimension = VSIR_DIMENSION_SCALAR;
     }
     else
     {
-        vsir_register_init(&dst->reg, type, VSIR_DATA_F32, 1);
+        vsir_operand_init(&dst->reg, type, VSIR_DATA_F32, 1);
         dst->reg.idx[0].offset = register_index;
         dst->reg.dimension = VSIR_DIMENSION_VEC4;
     }
@@ -10342,7 +10790,7 @@ static void sm1_generate_vsir_instr_mova(struct hlsl_ctx *ctx,
         return;
 
     dst = &ins->dst[0];
-    vsir_register_init(&dst->reg, VKD3DSPR_ADDR, VSIR_DATA_F32, 0);
+    vsir_operand_init(&dst->reg, VKD3DSPR_ADDR, VSIR_DATA_F32, 0);
     dst->write_mask = VKD3DSP_WRITEMASK_0;
 
     VKD3D_ASSERT(instr->data_type->class <= HLSL_CLASS_VECTOR);
@@ -10361,7 +10809,7 @@ static struct vsir_src_operand *sm1_generate_vsir_new_address_src(struct hlsl_ct
     }
 
     memset(idx_src, 0, sizeof(*idx_src));
-    vsir_register_init(&idx_src->reg, VKD3DSPR_ADDR, VSIR_DATA_F32, 0);
+    vsir_operand_init(&idx_src->reg, VKD3DSPR_ADDR, VSIR_DATA_F32, 0);
     idx_src->reg.dimension = VSIR_DIMENSION_VEC4;
     idx_src->swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X);
     return idx_src;
@@ -10586,6 +11034,11 @@ static void sm1_generate_vsir_instr_jump(struct hlsl_ctx *ctx,
 
         vsir_src_from_hlsl_node(&ins->src[0], ctx, condition, VKD3DSP_WRITEMASK_ALL);
     }
+    else if (jump->type == HLSL_IR_JUMP_BREAK)
+    {
+        if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_BREAK, 0, 0)))
+            return;
+    }
     else
     {
         hlsl_fixme(ctx, &instr->loc, "Jump type %s.", hlsl_jump_type_to_string(jump->type));
@@ -10629,6 +11082,33 @@ static void sm1_generate_vsir_instr_if(struct hlsl_ctx *ctx, struct vsir_program
         return;
 }
 
+static void sm1_generate_vsir_instr_loop(struct hlsl_ctx *ctx,
+        struct vsir_program *program, struct hlsl_ir_loop *loop)
+{
+    struct hlsl_ir_node *instr = &loop->node;
+    struct vkd3d_shader_instruction *ins;
+    struct vsir_src_operand *src;
+
+    if (loop->limiter)
+    {
+        hlsl_fixme(ctx, &instr->loc, "Loops with user-defined limiter int uniform.");
+        return;
+    }
+
+    if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_REP, 0, 1)))
+        return;
+    src = &ins->src[0];
+    vsir_src_operand_init(src, VKD3DSPR_CONSTINT, VSIR_DATA_I32, 1);
+    src->reg.dimension = VSIR_DIMENSION_VEC4;
+    src->swizzle = VKD3D_SHADER_NO_SWIZZLE;
+    src->reg.idx[0].offset = ctx->d3d255intconst.id;
+
+    sm1_generate_vsir_block(ctx, &loop->body, program);
+
+    if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_ENDREP, 0, 0)))
+        return;
+}
+
 static void sm1_generate_vsir_block(struct hlsl_ctx *ctx, struct hlsl_block *block, struct vsir_program *program)
 {
     struct hlsl_ir_node *instr, *next;
@@ -10681,6 +11161,10 @@ static void sm1_generate_vsir_block(struct hlsl_ctx *ctx, struct hlsl_block *blo
                 generate_vsir_instr_swizzle(ctx, program, hlsl_ir_swizzle(instr));
                 break;
 
+            case HLSL_IR_LOOP:
+                sm1_generate_vsir_instr_loop(ctx, program, hlsl_ir_loop(instr));
+                break;
+
             default:
                 hlsl_fixme(ctx, &instr->loc, "Instruction type %s.", hlsl_node_type_to_string(instr->type));
                 break;
@@ -11119,7 +11603,7 @@ static void ctab_write_uniforms(struct ctab_ctx *ctab)
                         switch (comp_type->e.numeric.type)
                         {
                             case HLSL_TYPE_DOUBLE:
-                                if (ctx->double_as_float_alias)
+                                if (ctx->compatibility_flags & VKD3D_SHADER_COMPILE_OPTION_DOUBLE_AS_FLOAT_ALIAS)
                                     uni.u = var->default_values[k].number.u;
                                 else
                                     uni.u = 0;
@@ -11258,7 +11742,7 @@ static void sm4_generate_vsir_instr_dcl_semantic(struct hlsl_ctx *ctx, struct vs
     uint32_t write_mask;
     bool has_idx;
 
-    sm4_sysval_semantic_from_semantic_name(&semantic, version, ctx->semantic_compat_mapping, ctx->domain,
+    sm4_sysval_semantic_from_semantic_name(&semantic, version, ctx->compatibility_flags, ctx->domain,
             var->semantic.name, var->semantic.index, output, ctx->is_patch_constant_func, is_primitive);
     if (semantic == ~0u)
         semantic = VKD3D_SHADER_SV_NONE;
@@ -11350,18 +11834,18 @@ static void sm4_generate_vsir_instr_dcl_semantic(struct hlsl_ctx *ctx, struct vs
     if (is_primitive)
     {
         VKD3D_ASSERT(has_idx);
-        vsir_register_init(&dst->reg, type, VSIR_DATA_F32, 2);
+        vsir_operand_init(&dst->reg, type, VSIR_DATA_F32, 2);
         dst->reg.idx[0].offset = var->data_type->e.array.elements_count;
         dst->reg.idx[1].offset = idx;
     }
     else if (has_idx)
     {
-        vsir_register_init(&dst->reg, type, VSIR_DATA_F32, 1);
+        vsir_operand_init(&dst->reg, type, VSIR_DATA_F32, 1);
         dst->reg.idx[0].offset = idx;
     }
     else
     {
-        vsir_register_init(&dst->reg, type, VSIR_DATA_F32, 0);
+        vsir_operand_init(&dst->reg, type, VSIR_DATA_F32, 0);
     }
 
     if (shader_sm4_is_scalar_register(&dst->reg))
@@ -12171,8 +12655,9 @@ static bool sm4_generate_vsir_instr_load(struct hlsl_ctx *ctx, struct vsir_progr
 static bool sm4_generate_vsir_instr_resource_store(struct hlsl_ctx *ctx,
         struct vsir_program *program, struct hlsl_ir_resource_store *store)
 {
+    struct hlsl_ir_node *coords = store->coords.node, *byte_offset = store->byte_offset.node;
     struct hlsl_type *resource_type = hlsl_deref_get_type(ctx, &store->resource);
-    struct hlsl_ir_node *coords = store->coords.node, *value = store->value.node;
+    struct hlsl_ir_node *value = store->value.node;
     struct hlsl_ir_node *instr = &store->node;
     bool tgsm = store->resource.var->is_tgsm;
     struct vkd3d_shader_instruction *ins;
@@ -12209,14 +12694,13 @@ static bool sm4_generate_vsir_instr_resource_store(struct hlsl_ctx *ctx,
         return false;
     }
 
-    if (tgsm && !hlsl_is_numeric_type(resource_type))
-    {
-        hlsl_fixme(ctx, &store->node.loc, "Store to structured TGSM.");
-        return false;
-    }
+    structured = resource_type->sampler_dim == HLSL_SAMPLER_DIM_STRUCTURED_BUFFER
+            || (tgsm && resource_type->class == HLSL_CLASS_ARRAY);
 
-    if ((structured = resource_type->sampler_dim == HLSL_SAMPLER_DIM_STRUCTURED_BUFFER))
+    if (structured)
     {
+        VKD3D_ASSERT(byte_offset);
+
         if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_STORE_STRUCTURED, 1, 3)))
             return false;
 
@@ -12224,7 +12708,7 @@ static bool sm4_generate_vsir_instr_resource_store(struct hlsl_ctx *ctx,
                 &store->resource, &instr->loc, store->writemask))
             return false;
 
-        vsir_src_from_hlsl_node(&ins->src[1], ctx, store->byte_offset.node, VKD3DSP_WRITEMASK_ALL);
+        vsir_src_from_hlsl_node(&ins->src[1], ctx, byte_offset, VKD3DSP_WRITEMASK_ALL);
     }
     else if (tgsm || resource_type->sampler_dim == HLSL_SAMPLER_DIM_RAW_BUFFER)
     {
@@ -12245,7 +12729,16 @@ static bool sm4_generate_vsir_instr_resource_store(struct hlsl_ctx *ctx,
             return false;
     }
 
-    vsir_src_from_hlsl_node(&ins->src[0], ctx, coords, VKD3DSP_WRITEMASK_ALL);
+    if (tgsm && !structured)
+    {
+        VKD3D_ASSERT(byte_offset);
+        vsir_src_from_hlsl_node(&ins->src[0], ctx, byte_offset, VKD3DSP_WRITEMASK_ALL);
+    }
+    else
+    {
+        vsir_src_from_hlsl_node(&ins->src[0], ctx, coords, VKD3DSP_WRITEMASK_ALL);
+    }
+
     vsir_src_from_hlsl_node(&ins->src[structured ? 2 : 1], ctx, value, VKD3DSP_WRITEMASK_ALL);
 
     return true;
@@ -12707,6 +13200,13 @@ static bool sm4_generate_vsir_instr_interlocked(struct hlsl_ctx *ctx,
     enum vkd3d_shader_opcode opcode;
     struct vsir_dst_operand *dst;
 
+    if (!interlocked->dst.var->is_tgsm && hlsl_deref_get_type(ctx, &interlocked->dst)->class != HLSL_CLASS_UAV)
+    {
+        hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                "Interlocked targets must be UAV or groupshared elements.");
+        return false;
+    }
+
     opcode = is_imm ? imm_opcodes[interlocked->op] : opcodes[interlocked->op];
 
     if (value->data_type->e.numeric.type == HLSL_TYPE_INT)
@@ -12784,6 +13284,19 @@ static bool sm4_generate_vsir_instr_sync(struct hlsl_ctx *ctx,
     const struct hlsl_ir_node *instr = &sync->node;
     struct vkd3d_shader_instruction *ins;
 
+    if (sync->sync_flags == VKD3DSSF_GLOBAL_UAV)
+    {
+       if (ctx->profile->type != VKD3D_SHADER_TYPE_COMPUTE && ctx->profile->type != VKD3D_SHADER_TYPE_PIXEL)
+           hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_INCOMPATIBLE_PROFILE,
+                   "DeviceMemoryBarrier() can only be used in compute and pixel shaders.");
+    }
+    else
+    {
+        if (ctx->profile->type != VKD3D_SHADER_TYPE_COMPUTE)
+            hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_INCOMPATIBLE_PROFILE,
+                    "Group barriers can only be used in compute shaders.");
+    }
+
     if (!(ins = generate_vsir_add_program_instruction(ctx, program, &instr->loc, VSIR_OP_SYNC, 0, 0)))
         return false;
     ins->flags = sync->sync_flags;
@@ -13683,8 +14196,8 @@ static void generate_vsir_descriptors_for_var(struct hlsl_ctx *ctx, struct vsir_
     for (unsigned int k = 0; k < component_count; ++k)
     {
         const struct hlsl_type *component_type = hlsl_type_get_component_type(ctx, var->data_type, k);
-        struct vkd3d_shader_register_range range;
         struct vkd3d_shader_descriptor_info1 *d;
+        struct vsir_register_range range;
         unsigned int regset_offset;
         enum hlsl_regset regset;
         uint32_t id;
@@ -13747,8 +14260,8 @@ static void generate_vsir_descriptors_for_var(struct hlsl_ctx *ctx, struct vsir_
 
 static void generate_vsir_descriptors(struct hlsl_ctx *ctx, struct vsir_program *program)
 {
-    struct vkd3d_shader_register_range range;
     struct vkd3d_shader_descriptor_info1 *d;
+    struct vsir_register_range range;
     const struct hlsl_ir_var *var;
 
     if (program->shader_version.major < 4)
@@ -13809,14 +14322,23 @@ static void generate_vsir_descriptors(struct hlsl_ctx *ctx, struct vsir_program
     program->normalisation_flags.has_descriptor_info = true;
 }
 
-/* For some reason, for matrices, values from default value initializers end
- * up in different components than from regular initializers. Default value
- * initializers fill the matrix in vertical reading order
- * (left-to-right top-to-bottom) instead of regular reading order
- * (top-to-bottom left-to-right), so they have to be adjusted. An exception is
- * that the order of matrix initializers for function parameters are row-major
- * (top-to-bottom left-to-right). */
-static unsigned int get_component_index_from_default_initializer_index(struct hlsl_type *type, unsigned int index)
+enum reading_order
+{
+    /* For some reason, for matrices, values from default value initializers end
+     * up in different components than from regular initializers. Default value
+     * initializers fill the matrix in vertical reading order
+     * (left-to-right top-to-bottom) instead of regular reading order
+     * (top-to-bottom left-to-right), so they have to be adjusted. An exception is
+     * that the order of matrix initializers for function parameters are row-major
+     * (top-to-bottom left-to-right). */
+    READING_ORDER_DEFAULT_VALUES,
+    /* Map the component index from column-major matrices as if they were
+     * row-major matrices. This is the order in which constint registers are
+     * assigned to matrix components in SM3. */
+    READING_ORDER_ALWAYS_ROW_MAJOR,
+};
+
+static unsigned int map_matrix_component_index(struct hlsl_type *type, unsigned int index, enum reading_order order)
 {
     unsigned int element_comp_count, element, x, y, i;
     unsigned int base = 0;
@@ -13824,15 +14346,20 @@ static unsigned int get_component_index_from_default_initializer_index(struct hl
     switch (type->class)
     {
         case HLSL_CLASS_MATRIX:
-            x = index / type->e.numeric.dimy;
-            y = index % type->e.numeric.dimy;
-            return y * type->e.numeric.dimx + x;
+            if ((order == READING_ORDER_ALWAYS_ROW_MAJOR && !hlsl_type_is_row_major(type))
+                    || order == READING_ORDER_DEFAULT_VALUES)
+            {
+                x = index / type->e.numeric.dimy;
+                y = index % type->e.numeric.dimy;
+                return y * type->e.numeric.dimx + x;
+            }
+            return index;
 
         case HLSL_CLASS_ARRAY:
             element_comp_count = hlsl_type_component_count(type->e.array.type);
             element = index / element_comp_count;
             base = element * element_comp_count;
-            return base + get_component_index_from_default_initializer_index(type->e.array.type, index - base);
+            return base + map_matrix_component_index(type->e.array.type, index - base, order);
 
         case HLSL_CLASS_STRUCT:
             for (i = 0; i < type->e.record.field_count; ++i)
@@ -13841,7 +14368,7 @@ static unsigned int get_component_index_from_default_initializer_index(struct hl
 
                 element_comp_count = hlsl_type_component_count(field_type);
                 if (index - base < element_comp_count)
-                    return base + get_component_index_from_default_initializer_index(field_type, index - base);
+                    return base + map_matrix_component_index(field_type, index - base, order);
                 base += element_comp_count;
             }
             break;
@@ -14402,7 +14929,7 @@ static void sm4_generate_rdef(struct hlsl_ctx *ctx, struct vkd3d_shader_code *ou
                         continue;
                     }
 
-                    comp_index = get_component_index_from_default_initializer_index(var->data_type, k);
+                    comp_index = map_matrix_component_index(var->data_type, k, READING_ORDER_DEFAULT_VALUES);
                     comp_offset = hlsl_type_get_component_offset(ctx, var->data_type, comp_index, &regset);
                     if (regset == HLSL_REGSET_NUMERIC)
                     {
@@ -14541,7 +15068,7 @@ static struct hlsl_ir_if *loop_unrolling_generate_var_check(struct hlsl_ctx *ctx
     load = hlsl_block_add_simple_load(ctx, dst, var, loc);
     cond = hlsl_block_add_unary_expr(ctx, dst, HLSL_OP1_LOGIC_NOT, load, loc);
 
-    if (!(iff = hlsl_new_if(ctx, cond, &then_block, NULL, HLSL_IF_FLATTEN_DEFAULT, loc)))
+    if (!(iff = hlsl_new_if(ctx, cond, &then_block, NULL, HLSL_IF_FLATTEN_DEFAULT, false, loc)))
         return NULL;
     hlsl_block_add_instr(dst, iff);
 
@@ -14632,6 +15159,174 @@ static void loop_unrolling_simplify(struct hlsl_ctx *ctx, struct hlsl_block *blo
     *index = current_index;
 }
 
+static struct hlsl_ir_if *find_loop_conditional(struct hlsl_block *block)
+{
+    struct hlsl_ir_node *instr;
+
+    LIST_FOR_EACH_ENTRY(instr, &block->instrs, struct hlsl_ir_node, entry)
+    {
+        if (instr->type == HLSL_IR_IF && hlsl_ir_if(instr)->is_loop_conditional)
+            return hlsl_ir_if(instr);
+    }
+
+    return NULL;
+}
+
+static bool hlsl_version_has_sm1_loop_support(struct hlsl_ctx *ctx)
+{
+    if (hlsl_version_ge(ctx, 4, 0))
+        return false;
+    if (ctx->profile->type == VKD3D_SHADER_TYPE_VERTEX)
+        return hlsl_version_ge(ctx, 2, 0);
+    return hlsl_version_ge(ctx, 2, 1);
+}
+
+static bool is_instr_constant_lt_var(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr,
+        unsigned int k, struct hlsl_ir_var **var, unsigned int *var_component, bool *negated)
+{
+    unsigned int component, component_count, swizzle_component = 0;
+    struct hlsl_ir_expr *expr;
+    struct hlsl_ir_node *arg1;
+    struct hlsl_ir_load *load;
+    bool floating = false;
+    bool neg = false;
+
+    if (instr->type != HLSL_IR_EXPR)
+        return false;
+    expr = hlsl_ir_expr(instr);
+
+    if (expr->op == HLSL_OP1_LOGIC_NOT)
+    {
+        if (expr->operands[0].node->type != HLSL_IR_EXPR)
+            return false;
+        expr = hlsl_ir_expr(expr->operands[0].node);
+        neg = true;
+    }
+
+    if (expr->op == HLSL_OP2_GEQUAL)
+        neg = !neg;
+    else if (expr->op != HLSL_OP2_LESS)
+        return false;
+
+    if (expr->operands[0].node->type != HLSL_IR_CONSTANT)
+        return false;
+
+    arg1 = expr->operands[1].node;
+    if (arg1->type == HLSL_IR_EXPR && hlsl_ir_expr(arg1)->op == HLSL_OP1_CAST)
+    {
+        if (hlsl_type_is_floating_point(arg1->data_type))
+            floating = true;
+        arg1 = hlsl_ir_expr(arg1)->operands[0].node;
+    }
+    if (arg1->type == HLSL_IR_SWIZZLE)
+    {
+        VKD3D_ASSERT(hlsl_ir_swizzle(arg1)->val.node->data_type->class == HLSL_CLASS_VECTOR);
+
+        swizzle_component = hlsl_swizzle_get_component(hlsl_ir_swizzle(arg1)->u.vector, 0);
+        arg1 = hlsl_ir_swizzle(arg1)->val.node;
+    }
+    if (arg1->type != HLSL_IR_LOAD)
+        return false;
+    if (!floating && hlsl_ir_constant(expr->operands[0].node)->value.u[0].u != k)
+        return false;
+    if (floating && hlsl_ir_constant(expr->operands[0].node)->value.u[0].f != k)
+        return false;
+
+    load = hlsl_ir_load(arg1);
+    if (load->node.data_type->e.numeric.type != HLSL_TYPE_INT
+            && load->node.data_type->e.numeric.type != HLSL_TYPE_UINT)
+        return false;
+    *var = load->src.var;
+
+    if (!hlsl_component_index_range_from_deref(ctx, &load->src, &component, &component_count))
+        return false;
+    component += swizzle_component;
+    component = map_matrix_component_index((*var)->data_type, component, READING_ORDER_ALWAYS_ROW_MAJOR);
+
+    *var_component = component;
+    *negated = neg;
+    return true;
+}
+
+/* In SM1, the native compiler will emit REP or LOOP instructions to represent
+ * loops. These instructions require an i# register containing the number of
+ * iterations.
+ *
+ * When the compiler is able to determine that the number of iterations depends
+ * of an int uniform, it will expect the user to provide its value on an i#
+ * register instead of defining it in the program using DEFI. We call this
+ * uniform the "limiter".
+ *
+ * To detect such cases, instead of simply pattern-matching loops like
+ * "for (i = 0; i < n; ++i)", native apparently tests that the condition
+ * ultimately matches 'k < n' for the k-th iteration of the loop, for all k
+ * from 0 to 255.
+ *
+ * This means that complex conditions like "for (i = 0; i % 255 < n; ++i)" and
+ * "for (i = 0; i / 2 < n; i += 2)" will use a user-provided i# uniform, so will
+ * conditions like "for (; i < n;)" if i is initialized to 0 before the loop
+ * and incremented by 1 inside it.
+ * Interestingly, something as simple as "for (i = 1; i <= n; ++i)" doesn't
+ * match, so it doesn't use a user-provided i# uniform.
+ *
+ * The choice of whether a uniform is placed in c# or i# (or both) is a
+ * relatively user-visible detail, that also affects the registers on which
+ * other uniforms are allocated, so it's important to try to replicate the
+ * same logic.
+ *
+ * For a block of instructions containing a singular, lowered, iteration of a
+ * loop, this function checks if the loop conditional on that iteration has the
+ * form 'k < n', where k is a constant.
+ *
+ * If we find that the condition matches, we track the uniform (n) as the
+ * loop's limiter. On the other hand, if the condition ever fails, or matches
+ * for another uniform (or another component of the same one), then we unset it,
+ * return NULL and fail. */
+static struct hlsl_ir_if *identify_limiter_uniform(struct hlsl_ctx *ctx,
+        struct hlsl_ir_loop *loop, struct hlsl_block *block, unsigned int k)
+{
+    struct hlsl_ir_node *condition;
+    struct hlsl_ir_if *iff = NULL;
+    unsigned int var_component;
+    struct hlsl_ir_var *var;
+    bool negated;
+
+    VKD3D_ASSERT(loop->unroll_type == HLSL_LOOP_FORCE_LOOP);
+    VKD3D_ASSERT(loop->type != HLSL_LOOP_DO_WHILE);
+
+    /* If we don't find the loop conditional, this means that either this
+     * whole iteration is guaranteed, so the loop is not limited by our
+     * current limiter candidate, or that the loop always ends in this
+     * iteration for conditions other than the limiter itself. In the latter
+     * case, fxc/d3dcompiler doesn't invalidate the limiter. */
+    if (!(iff = find_loop_conditional(block)))
+        return NULL;
+
+    condition = iff->condition.node;
+    if (!is_instr_constant_lt_var(ctx, condition, k, &var, &var_component, &negated))
+        goto fail;
+    if (!negated)
+        goto fail;
+
+    if (!var->is_uniform)
+        goto fail;
+
+    if (!loop->limiter)
+    {
+        loop->limiter = var;
+        loop->limiter_component = var_component;
+    }
+
+    if (loop->limiter != var || loop->limiter_component != var_component)
+        goto fail;
+
+    return iff;
+
+fail:
+    loop->limiter = NULL;
+    return NULL;
+}
+
 static bool loop_unrolling_check_val(struct copy_propagation_state *state, struct hlsl_ir_var *var)
 {
     struct copy_propagation_value *v;
@@ -14643,8 +15338,8 @@ static bool loop_unrolling_check_val(struct copy_propagation_state *state, struc
     return hlsl_ir_constant(v->node)->value.u[0].u;
 }
 
-static bool loop_unrolling_unroll_loop(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_loop *loop,
-        unsigned int unroll_limit)
+static bool loop_unrolling_unroll_loop(struct hlsl_ctx *ctx, struct hlsl_block *block,
+        struct hlsl_ir_loop *loop, unsigned int unroll_limit, bool identify_limiter)
 {
     struct hlsl_block draft, tmp_dst, loop_body;
     struct hlsl_ir_var *broken, *continued;
@@ -14708,6 +15403,26 @@ static bool loop_unrolling_unroll_loop(struct hlsl_ctx *ctx, struct hlsl_block *
 
         loop_unrolling_simplify(ctx, &target_if->then_block, &state, &index);
 
+        if (identify_limiter)
+        {
+            struct hlsl_ir_if *limiter_if;
+
+            if (!(limiter_if = identify_limiter_uniform(ctx, loop, &target_if->then_block, i)))
+            {
+                loop_unrolling_simplify(ctx, &target_if->then_block, &state, &index);
+                if (!loop_unrolling_check_val(&state, broken))
+                    loop->limiter = NULL;
+                goto fail;
+            }
+
+            VKD3D_ASSERT(list_empty(&limiter_if->else_block.instrs));
+            /* Remove the conditional so the next iteration can assume that this
+             * one was completed while lowering. */
+            list_remove(&limiter_if->node.entry);
+            hlsl_free_instr(&limiter_if->node);
+            loop_unrolling_simplify(ctx, &target_if->then_block, &state, &index);
+        }
+
         if (loop_unrolling_check_val(&state, broken))
             break;
 
@@ -14731,6 +15446,9 @@ static bool loop_unrolling_unroll_loop(struct hlsl_ctx *ctx, struct hlsl_block *
         goto fail;
     }
 
+    if (identify_limiter)
+        goto fail;
+
     hlsl_block_cleanup(&loop_body);
     copy_propagation_state_destroy(&state);
 
@@ -14779,12 +15497,30 @@ static bool unroll_loops(struct hlsl_ctx *ctx, struct hlsl_ir_node *node, void *
         unroll_limit = c->value.u[0].u;
     }
 
-    if (!loop_unrolling_unroll_loop(ctx, program, loop, unroll_limit))
+    if (!loop_unrolling_unroll_loop(ctx, program, loop, unroll_limit, false))
         loop->unroll_type = HLSL_LOOP_FORCE_LOOP;
 
     return true;
 }
 
+static bool identify_loop_limiters(struct hlsl_ctx *ctx, struct hlsl_ir_node *node, void *context)
+{
+    struct hlsl_block *program = context;
+    struct hlsl_ir_loop *loop;
+
+    if (node->type != HLSL_IR_LOOP)
+        return false;
+
+    loop = hlsl_ir_loop(node);
+
+    if (loop->type == HLSL_LOOP_DO_WHILE)
+        return false;
+    if (loop->unroll_type == HLSL_LOOP_FORCE_LOOP)
+        loop_unrolling_unroll_loop(ctx, program, loop, 255, true);
+
+    return false;
+}
+
 /* We could handle this at parse time. However, loop unrolling often needs to
  * know the value of variables modified in the "iter" block. It is possible to
  * detect that all exit paths of a loop body modify such variables in the same
@@ -14891,6 +15627,8 @@ static void loop_unrolling_execute(struct hlsl_ctx *ctx, struct hlsl_block *bloc
         if (!hlsl_transform_ir_once(ctx, unroll_loops, block, block))
             break;
     }
+    if (hlsl_version_has_sm1_loop_support(ctx))
+        hlsl_transform_ir(ctx, identify_loop_limiters, block, block);
     resolve_continues(ctx, block, NULL);
     hlsl_transform_ir(ctx, resolve_loops, block, NULL);
 }
@@ -15324,7 +16062,9 @@ static void process_entry_function(struct hlsl_ctx *ctx, struct list *semantic_v
     replace_ir(ctx, lower_tgsm_loads, body);
     hlsl_lower_index_loads(ctx, body);
 
-    replace_ir(ctx, lower_tgsm_stores, body);
+    split_copies(ctx, body);
+    replace_ir(ctx, lower_resource_stores, body);
+    replace_ir(ctx, generate_interlocked_coords, body);
 
     if (entry_func->return_var)
     {
@@ -15497,7 +16237,7 @@ static void process_entry_function(struct hlsl_ctx *ctx, struct list *semantic_v
     remove_unreachable_code(ctx, body);
     hlsl_transform_ir(ctx, normalize_switch_cases, body, NULL);
 
-    replace_ir(ctx, lower_nonconstant_vector_derefs, body);
+    replace_ir(ctx, lower_vector_derefs, body);
     replace_ir(ctx, lower_casts_to_bool, body);
     replace_ir(ctx, lower_int_dot, body);
 
@@ -15544,7 +16284,6 @@ static void process_entry_function(struct hlsl_ctx *ctx, struct list *semantic_v
 
         hlsl_transform_ir(ctx, cast_discard_neg_conditions_to_vec4, body, NULL);
 
-        replace_ir(ctx, lower_ternary, body);
         replace_ir(ctx, lower_int_modulus_sm1, body);
         replace_ir(ctx, lower_division, body);
         /* Constants casted to float must be folded, and new casts to bool also need to be lowered. */
@@ -15556,8 +16295,6 @@ static void process_entry_function(struct hlsl_ctx *ctx, struct list *semantic_v
         replace_ir(ctx, lower_sqrt, body);
         replace_ir(ctx, lower_dot, body);
         replace_ir(ctx, lower_round, body);
-        replace_ir(ctx, lower_ceil, body);
-        replace_ir(ctx, lower_floor, body);
         replace_ir(ctx, lower_trig, body);
         replace_ir(ctx, lower_comparison_operators, body);
         replace_ir(ctx, lower_logic_not, body);
@@ -15576,14 +16313,6 @@ static void process_entry_function(struct hlsl_ctx *ctx, struct list *semantic_v
 
     hlsl_run_folding_passes(ctx, body);
 
-    if (profile->major_version < 4)
-    {
-        /* Ternary operations can be potentially introduced by hlsl_run_folding_passes(). */
-        replace_ir(ctx, lower_ternary, body);
-        if (ctx->profile->type != VKD3D_SHADER_TYPE_PIXEL)
-            replace_ir(ctx, lower_cmp, body);
-    }
-
     do
         compute_liveness(ctx, body);
     while (hlsl_transform_ir(ctx, dce, body, NULL));
@@ -15620,6 +16349,7 @@ int hlsl_emit_vsir(struct hlsl_ctx *ctx, const struct vkd3d_shader_compile_info
     struct list semantic_vars, patch_semantic_vars;
     struct hlsl_ir_var *var;
 
+    hlsl_ctx_init_entry_function_attributes(ctx);
     parse_entry_function_attributes(ctx, entry_func);
     if (ctx->result)
         return ctx->result;
@@ -15653,8 +16383,16 @@ int hlsl_emit_vsir(struct hlsl_ctx *ctx, const struct vkd3d_shader_compile_info
         }
         else if (var->storage_modifiers & HLSL_STORAGE_GROUPSHARED)
         {
-            var->is_tgsm = 1;
-            list_add_tail(&ctx->extern_vars, &var->extern_entry);
+            if (profile->type == VKD3D_SHADER_TYPE_COMPUTE)
+            {
+                var->is_tgsm = 1;
+                list_add_tail(&ctx->extern_vars, &var->extern_entry);
+            }
+            else
+            {
+                hlsl_warning(ctx, &var->loc, VKD3D_SHADER_WARNING_HLSL_IGNORED_MODIFIER,
+                        "Ignoring the 'groupshared' modifier in a non-compute shader.");
+            }
         }
     }
 
diff --git a/libs/vkd3d/libs/vkd3d-shader/hlsl_constant_ops.c b/libs/vkd3d/libs/vkd3d-shader/hlsl_constant_ops.c
index e6edbe598bc2..e1bbcac7589f 100644
--- a/libs/vkd3d/libs/vkd3d-shader/hlsl_constant_ops.c
+++ b/libs/vkd3d/libs/vkd3d-shader/hlsl_constant_ops.c
@@ -18,9 +18,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#include <math.h>
-
 #include "hlsl.h"
+#include <math.h>
 
 static bool fold_abs(struct hlsl_ctx *ctx, struct hlsl_constant_value *dst,
         const struct hlsl_type *dst_type, const struct hlsl_ir_constant *src)
@@ -1286,6 +1285,7 @@ static bool fold_mod(struct hlsl_ctx *ctx, struct hlsl_constant_value *dst, cons
 {
     enum hlsl_base_type type = dst_type->e.numeric.type;
     unsigned int k;
+    float x, y;
 
     VKD3D_ASSERT(type == src1->node.data_type->e.numeric.type);
     VKD3D_ASSERT(type == src2->node.data_type->e.numeric.type);
@@ -1294,6 +1294,22 @@ static bool fold_mod(struct hlsl_ctx *ctx, struct hlsl_constant_value *dst, cons
     {
         switch (type)
         {
+            /* Explicitly disable floating-point contraction on Clang to
+             * prevent it from fusing the multiplication and the
+             * addition/subtraction below. Current versions of GCC
+             * unfortunately do no support the FP_CONTRACT pragma, but don't
+             * contract the expression either. */
+#ifdef __clang__
+#pragma STDC FP_CONTRACT OFF
+#endif
+            case HLSL_TYPE_FLOAT:
+            case HLSL_TYPE_HALF:
+                x = src1->value.u[k].f;
+                y = src2->value.u[k].f;
+                /* Explicit cast to float to avoid x87 excess precision. */
+                dst->u[k].f = x - (float)(truncf(x / y) * y);
+                break;
+
             case HLSL_TYPE_INT:
                 if (src2->value.u[k].i == 0)
                 {
diff --git a/libs/vkd3d/libs/vkd3d-shader/ir.c b/libs/vkd3d/libs/vkd3d-shader/ir.c
index 3c5167c62662..3ab3dbd4efe2 100644
--- a/libs/vkd3d/libs/vkd3d-shader/ir.c
+++ b/libs/vkd3d/libs/vkd3d-shader/ir.c
@@ -19,6 +19,7 @@
 
 #include "vkd3d_shader_private.h"
 #include "vkd3d_types.h"
+#include "wine/rbtree.h"
 
 struct vsir_transformation_context
 {
@@ -76,13 +77,13 @@ const char *vsir_data_type_get_name(enum vsir_data_type t, const char *error)
     return error;
 }
 
-const char *vsir_denorm_mode_get_name(enum vsir_denorm_mode m, const char *error)
+const char *vsir_denorm_mode_get_name(enum vkd3d_shader_denormal_mode m, const char *error)
 {
     static const char * const names[] =
     {
-        [VSIR_DENORM_ANY]           = "any",
-        [VSIR_DENORM_PRESERVE]      = "preserve",
-        [VSIR_DENORM_FLUSH_TO_ZERO] = "ftz",
+        [VKD3D_SHADER_DENORMAL_MODE_ANY]           = "any",
+        [VKD3D_SHADER_DENORMAL_MODE_PRESERVE]      = "preserve",
+        [VKD3D_SHADER_DENORMAL_MODE_FLUSH_TO_ZERO] = "ftz",
     };
 
     if ((size_t)m < ARRAY_SIZE(names))
@@ -91,6 +92,72 @@ const char *vsir_denorm_mode_get_name(enum vsir_denorm_mode m, const char *error
     return error;
 }
 
+const char *vsir_register_type_get_name(enum vkd3d_shader_register_type type, const char *error)
+{
+    static const char * const names[] =
+    {
+        [VKD3DSPR_ADDR              ] = "a",
+        [VKD3DSPR_ATTROUT           ] = "oD",
+        [VKD3DSPR_COLOROUT          ] = "oC",
+        [VKD3DSPR_COMBINED_SAMPLER  ] = "s",
+        [VKD3DSPR_CONST             ] = "c",
+        [VKD3DSPR_CONSTBOOL         ] = "b",
+        [VKD3DSPR_CONSTBUFFER       ] = "cb",
+        [VKD3DSPR_CONSTINT          ] = "i",
+        [VKD3DSPR_COVERAGE          ] = "vCoverage",
+        [VKD3DSPR_DEPTHOUT          ] = "oDepth",
+        [VKD3DSPR_DEPTHOUTGE        ] = "oDepthGE",
+        [VKD3DSPR_DEPTHOUTLE        ] = "oDepthLE",
+        [VKD3DSPR_FORKINSTID        ] = "vForkInstanceId",
+        [VKD3DSPR_FUNCTIONBODY      ] = "fb",
+        [VKD3DSPR_FUNCTIONPOINTER   ] = "fp",
+        [VKD3DSPR_GROUPSHAREDMEM    ] = "g",
+        [VKD3DSPR_GSINSTID          ] = "vGSInstanceID",
+        [VKD3DSPR_IDXTEMP           ] = "x",
+        [VKD3DSPR_IMMCONST          ] = "l",
+        [VKD3DSPR_IMMCONST64        ] = "d",
+        [VKD3DSPR_IMMCONSTBUFFER    ] = "icb",
+        [VKD3DSPR_INCONTROLPOINT    ] = "vicp",
+        [VKD3DSPR_INPUT             ] = "v",
+        [VKD3DSPR_JOININSTID        ] = "vJoinInstanceId",
+        [VKD3DSPR_LABEL             ] = "l",
+        [VKD3DSPR_LOCALTHREADID     ] = "vThreadIDInGroup",
+        [VKD3DSPR_LOCALTHREADINDEX  ] = "vThreadIDInGroupFlattened",
+        [VKD3DSPR_LOOP              ] = "aL",
+        [VKD3DSPR_NULL              ] = "null",
+        [VKD3DSPR_OUTCONTROLPOINT   ] = "vocp",
+        [VKD3DSPR_OUTPOINTID        ] = "vOutputControlPointID",
+        [VKD3DSPR_OUTPUT            ] = "o",
+        [VKD3DSPR_OUTSTENCILREF     ] = "oStencilRef",
+        [VKD3DSPR_PARAMETER         ] = "parameter",
+        [VKD3DSPR_PATCHCONST        ] = "vpc",
+        [VKD3DSPR_POINT_COORD       ] = "vPointCoord",
+        [VKD3DSPR_PREDICATE         ] = "p",
+        [VKD3DSPR_PRIMID            ] = "primID",
+        [VKD3DSPR_RASTERIZER        ] = "rasterizer",
+        [VKD3DSPR_RESOURCE          ] = "t",
+        [VKD3DSPR_SAMPLEMASK        ] = "oMask",
+        [VKD3DSPR_SAMPLER           ] = "s",
+        [VKD3DSPR_SSA               ] = "sr",
+        [VKD3DSPR_STREAM            ] = "m",
+        [VKD3DSPR_TEMP              ] = "r",
+        [VKD3DSPR_TESSCOORD         ] = "vDomainLocation",
+        [VKD3DSPR_TEXCRDOUT         ] = "oT",
+        [VKD3DSPR_TEXTURE           ] = "t",
+        [VKD3DSPR_THREADGROUPID     ] = "vThreadGroupID",
+        [VKD3DSPR_THREADID          ] = "vThreadID",
+        [VKD3DSPR_UAV               ] = "u",
+        [VKD3DSPR_UNDEF             ] = "undef",
+        [VKD3DSPR_WAVELANECOUNT     ] = "vWaveLaneCount",
+        [VKD3DSPR_WAVELANEINDEX     ] = "vWaveLaneIndex",
+    };
+
+    if ((uint32_t)type < ARRAY_SIZE(names) && names[type])
+        return names[type];
+
+    return error;
+}
+
 const char *vsir_opcode_get_name(enum vkd3d_shader_opcode op, const char *error)
 {
     static const char * const names[] =
@@ -560,7 +627,7 @@ bool vsir_program_add_icb(struct vsir_program *program, struct vkd3d_shader_imme
 static struct vsir_src_operand *vsir_program_clone_src_operands(
         struct vsir_program *program, const struct vsir_src_operand *operands, size_t count);
 
-static bool shader_register_clone_relative_addresses(struct vkd3d_shader_register *reg, struct vsir_program *program)
+static bool vsir_operand_clone_indirect_indices(struct vsir_operand *reg, struct vsir_program *program)
 {
     size_t i;
 
@@ -588,7 +655,7 @@ static struct vsir_dst_operand *vsir_program_clone_dst_operands(
     memcpy(ret, operands, count * sizeof(*operands));
     for (i = 0; i < count; ++i)
     {
-        if (!shader_register_clone_relative_addresses(&ret[i].reg, program))
+        if (!vsir_operand_clone_indirect_indices(&ret[i].reg, program))
             return NULL;
     }
 
@@ -607,7 +674,7 @@ static struct vsir_src_operand *vsir_program_clone_src_operands(
     memcpy(ret, operands, count * sizeof(*operands));
     for (i = 0; i < count; ++i)
     {
-        if (!shader_register_clone_relative_addresses(&ret[i].reg, program))
+        if (!vsir_operand_clone_indirect_indices(&ret[i].reg, program))
             return NULL;
     }
 
@@ -798,8 +865,7 @@ unsigned int vsir_signature_next_location(const struct shader_signature *signatu
 }
 
 struct vkd3d_shader_descriptor_info1 *vsir_program_add_descriptor(struct vsir_program *program,
-        enum vkd3d_shader_descriptor_type type, unsigned int register_id,
-        const struct vkd3d_shader_register_range *range,
+        enum vkd3d_shader_descriptor_type type, unsigned int register_id, const struct vsir_register_range *range,
         enum vkd3d_shader_resource_type resource_type, enum vsir_data_type resource_data_type)
 {
     struct vkd3d_shader_scan_descriptor_info1 *info = &program->descriptors;
@@ -876,7 +942,7 @@ static uint32_t vsir_combine_swizzles(uint32_t first, uint32_t second)
     return ret;
 }
 
-void vsir_register_init(struct vkd3d_shader_register *reg, enum vkd3d_shader_register_type reg_type,
+void vsir_operand_init(struct vsir_operand *reg, enum vkd3d_shader_register_type reg_type,
         enum vsir_data_type data_type, unsigned int idx_count)
 {
     reg->type = reg_type;
@@ -897,7 +963,7 @@ void vsir_register_init(struct vkd3d_shader_register *reg, enum vkd3d_shader_reg
     reg->alignment = 0;
 }
 
-static inline bool shader_register_is_phase_instance_id(const struct vkd3d_shader_register *reg)
+static bool vsir_operand_is_phase_instance_id(const struct vsir_operand *reg)
 {
     return reg->type == VKD3DSPR_FORKINSTID || reg->type == VKD3DSPR_JOININSTID;
 }
@@ -905,9 +971,10 @@ static inline bool shader_register_is_phase_instance_id(const struct vkd3d_shade
 void vsir_src_operand_init(struct vsir_src_operand *src, enum vkd3d_shader_register_type reg_type,
         enum vsir_data_type data_type, unsigned int idx_count)
 {
-    vsir_register_init(&src->reg, reg_type, data_type, idx_count);
+    vsir_operand_init(&src->reg, reg_type, data_type, idx_count);
     src->swizzle = 0;
     src->modifiers = VKD3DSPSM_NONE;
+    src->owner_index = 0;
 }
 
 static void vsir_src_operand_init_const_f32(struct vsir_src_operand *src, float value)
@@ -997,7 +1064,7 @@ static void vsir_src_operand_init_ssa_f32(struct vsir_src_operand *src, unsigned
     vsir_src_operand_init_ssa_scalar(src, idx, VSIR_DATA_F32);
 }
 
-static void vsir_src_operand_init_ssa_f32v4(struct vsir_src_operand *src, unsigned int idx)
+void vsir_src_operand_init_ssa_f32v4(struct vsir_src_operand *src, unsigned int idx)
 {
     vsir_src_operand_init_ssa(src, idx, VSIR_DATA_F32, VSIR_DIMENSION_VEC4);
 }
@@ -1031,7 +1098,7 @@ static void vsir_src_operand_init_temp_u32(struct vsir_src_operand *src, unsigne
 void vsir_dst_operand_init(struct vsir_dst_operand *dst, enum vkd3d_shader_register_type reg_type,
         enum vsir_data_type data_type, unsigned int idx_count)
 {
-    vsir_register_init(&dst->reg, reg_type, data_type, idx_count);
+    vsir_operand_init(&dst->reg, reg_type, data_type, idx_count);
     dst->write_mask = VKD3DSP_WRITEMASK_0;
     dst->modifiers = VKD3DSPDM_NONE;
     dst->shift = 0;
@@ -1081,7 +1148,7 @@ static void vsir_dst_operand_init_ssa_f32(struct vsir_dst_operand *dst, unsigned
     vsir_dst_operand_init_ssa_scalar(dst, idx, VSIR_DATA_F32);
 }
 
-static void vsir_dst_operand_init_ssa_f32v4(struct vsir_dst_operand *dst, unsigned int idx)
+void vsir_dst_operand_init_ssa_f32v4(struct vsir_dst_operand *dst, unsigned int idx)
 {
     vsir_dst_operand_init_ssa(dst, idx, VSIR_DATA_F32, VSIR_DIMENSION_VEC4);
 }
@@ -1252,7 +1319,7 @@ static enum vkd3d_result vsir_program_normalize_addr(struct vsir_program *progra
                 tmp_idx = program->temp_count++;
 
             ins->opcode = VSIR_OP_FTOU;
-            vsir_register_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
+            vsir_operand_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
             ins->dst[0].reg.idx[0].offset = tmp_idx;
             ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
         }
@@ -1266,7 +1333,7 @@ static enum vkd3d_result vsir_program_normalize_addr(struct vsir_program *progra
             ins = vsir_program_iterator_current(&it);
 
             ins->opcode = VSIR_OP_ROUND_NE;
-            vsir_register_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_F32, 1);
+            vsir_operand_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_F32, 1);
             ins->dst[0].reg.idx[0].offset = tmp_idx;
             ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
 
@@ -1274,13 +1341,13 @@ static enum vkd3d_result vsir_program_normalize_addr(struct vsir_program *progra
             if (!vsir_instruction_init_with_params(program, ins2, &ins->location, VSIR_OP_FTOU, 1, 1))
                 return VKD3D_ERROR_OUT_OF_MEMORY;
 
-            vsir_register_init(&ins2->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
+            vsir_operand_init(&ins2->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
             ins2->dst[0].reg.idx[0].offset = tmp_idx;
             ins2->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
             ins2->dst[0].write_mask = ins->dst[0].write_mask;
             ins->dst[0].write_mask = VKD3DSP_WRITEMASK_ALL;
 
-            vsir_register_init(&ins2->src[0].reg, VKD3DSPR_TEMP, VSIR_DATA_F32, 1);
+            vsir_operand_init(&ins2->src[0].reg, VKD3DSPR_TEMP, VSIR_DATA_F32, 1);
             ins2->src[0].reg.idx[0].offset = tmp_idx;
             ins2->src[0].reg.dimension = VSIR_DIMENSION_VEC4;
             ins2->src[0].swizzle = VKD3D_SHADER_NO_SWIZZLE;
@@ -1299,7 +1366,7 @@ static enum vkd3d_result vsir_program_normalize_addr(struct vsir_program *progra
                     if (tmp_idx == ~0u)
                         tmp_idx = program->temp_count++;
 
-                    vsir_register_init(&rel->reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
+                    vsir_operand_init(&rel->reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
                     rel->reg.idx[0].offset = tmp_idx;
                     rel->reg.dimension = VSIR_DIMENSION_VEC4;
                 }
@@ -1376,7 +1443,7 @@ static enum vkd3d_result vsir_program_lower_ifc(struct vsir_program *program,
     if (!vsir_instruction_init_with_params(program, ins, &ifc->location, opcode, 1, 2))
         return VKD3D_ERROR_OUT_OF_MEMORY;
 
-    vsir_register_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
+    vsir_operand_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
     ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
     ins->dst[0].reg.idx[0].offset = *tmp_idx;
     ins->dst[0].write_mask = VKD3DSP_WRITEMASK_0;
@@ -1390,7 +1457,7 @@ static enum vkd3d_result vsir_program_lower_ifc(struct vsir_program *program,
         return VKD3D_ERROR_OUT_OF_MEMORY;
     ins->flags = VKD3D_SHADER_CONDITIONAL_OP_NZ;
 
-    vsir_register_init(&ins->src[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
+    vsir_operand_init(&ins->src[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
     ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4;
     ins->src[0].reg.idx[0].offset = *tmp_idx;
     ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X);
@@ -1440,6 +1507,9 @@ static enum vkd3d_result vsir_program_lower_lrp(struct vsir_program *program, st
     ins->src[0] = src[0];
     ins->src[1] = src[1];
     vsir_src_operand_init_ssa(&ins->src[2], mad_id, src[2].reg.data_type, src[2].reg.dimension);
+    /* Copy rel_addrs in second use of SRC0, to avoid multiple owners. */
+    if (!vsir_operand_clone_indirect_indices(&ins->src[0].reg, program))
+        goto fail;
 
     return VKD3D_OK;
 
@@ -1449,6 +1519,84 @@ fail:
     return VKD3D_ERROR_OUT_OF_MEMORY;
 }
 
+static enum vkd3d_result vsir_program_lower_mnxn(struct vsir_program *program, struct vsir_program_iterator *mnxn)
+{
+    struct vkd3d_shader_instruction *ins = vsir_program_iterator_current(mnxn);
+    const struct vkd3d_shader_location location = ins->location;
+    const struct vsir_src_operand *src = ins->src;
+    const struct vsir_dst_operand *dst = ins->dst;
+    struct vsir_program_iterator it;
+    enum vkd3d_shader_opcode opcode;
+    unsigned int reg_count;
+
+    /* mNxM DST, SRC0, SRC1
+     *      ->
+     * dpN DST.x, SRC0, SRC1
+     * ...
+     * dpN DST.(M-1), SRC0, SRC1+(M-1) */
+
+    switch (ins->opcode)
+    {
+        case VSIR_OP_M3x2:
+            reg_count = 2;
+            opcode = VSIR_OP_DP3;
+            break;
+        case VSIR_OP_M3x3:
+            reg_count = 3;
+            opcode = VSIR_OP_DP3;
+            break;
+        case VSIR_OP_M3x4:
+            reg_count = 4;
+            opcode = VSIR_OP_DP3;
+            break;
+        case VSIR_OP_M4x3:
+            reg_count = 3;
+            opcode = VSIR_OP_DP4;
+            break;
+        case VSIR_OP_M4x4:
+            reg_count = 4;
+            opcode = VSIR_OP_DP4;
+            break;
+        default:
+            vkd3d_unreachable();
+    }
+
+    VKD3D_ASSERT(dst[0].write_mask == ((1u << reg_count) - 1));
+    VKD3D_ASSERT(src[1].reg.idx_count == 1);
+
+    if (!(ins = vsir_program_iterator_insert_before(mnxn, &it, reg_count - 1)))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+
+    for (unsigned int i = 0; i < reg_count; ++i)
+    {
+        if (!vsir_instruction_init_with_params(program, ins, &location, opcode, 1, 2))
+            goto fail;
+        ins->dst[0] = dst[0];
+        ins->dst[0].write_mask = (VKD3DSP_WRITEMASK_0 << i);
+
+        ins->src[0] = src[0];
+        ins->src[1] = src[1];
+        ins->src[1].reg.idx[0].offset += i;
+
+        /* Copy rel_addrs to avoid multiple owners. */
+        if (!vsir_operand_clone_indirect_indices(&ins->dst[0].reg, program))
+            goto fail;
+        if (!vsir_operand_clone_indirect_indices(&ins->src[0].reg, program))
+            goto fail;
+        if (!vsir_operand_clone_indirect_indices(&ins->src[1].reg, program))
+            goto fail;
+
+        ins = vsir_program_iterator_next(&it);
+    }
+
+    return VKD3D_OK;
+
+fail:
+    vsir_program_iterator_nop_range(&it, mnxn, &location);
+
+    return VKD3D_ERROR_OUT_OF_MEMORY;
+}
+
 static enum vkd3d_result vsir_program_lower_nrm(struct vsir_program *program, struct vsir_program_iterator *nrm)
 {
     struct vkd3d_shader_instruction *ins = vsir_program_iterator_current(nrm);
@@ -1562,6 +1710,9 @@ static enum vkd3d_result vsir_program_lower_pow(struct vsir_program *program, st
     vsir_dst_operand_init_ssa(&ins->dst[0], mul_id, src[0].reg.data_type, VSIR_DIMENSION_SCALAR);
     vsir_src_operand_init_ssa(&ins->src[0], movc_id, src[0].reg.data_type, VSIR_DIMENSION_SCALAR);
     ins->src[1] = src[1];
+    /* Copy rel_addrs in second use of SRC1, to avoid multiple owners. */
+    if (!vsir_operand_clone_indirect_indices(&ins->src[1].reg, program))
+        goto fail;
 
     ins = vsir_program_iterator_next(&it);
     if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_EXP, 1, 1))
@@ -1597,14 +1748,14 @@ static enum vkd3d_result vsir_program_lower_texkill(struct vsir_program *program
     if (!vsir_instruction_init_with_params(program, ins, &texkill->location, VSIR_OP_LTO, 1, 2))
         return VKD3D_ERROR_OUT_OF_MEMORY;
 
-    vsir_register_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
+    vsir_operand_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
     ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
     ins->dst[0].reg.idx[0].offset = *tmp_idx;
     ins->dst[0].write_mask = VKD3DSP_WRITEMASK_ALL;
 
     ins->src[0].reg = texkill->src[0].reg;
     ins->src[0].swizzle = VKD3D_SHADER_NO_SWIZZLE;
-    vsir_register_init(&ins->src[1].reg, VKD3DSPR_IMMCONST, VSIR_DATA_F32, 0);
+    vsir_operand_init(&ins->src[1].reg, VKD3DSPR_IMMCONST, VSIR_DATA_F32, 0);
     ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4;
     ins->src[1].reg.u.immconst_f32[0] = 0.0f;
     ins->src[1].reg.u.immconst_f32[1] = 0.0f;
@@ -1622,16 +1773,16 @@ static enum vkd3d_result vsir_program_lower_texkill(struct vsir_program *program
         if (!(vsir_instruction_init_with_params(program, ins, &texkill->location, VSIR_OP_OR, 1, 2)))
             return VKD3D_ERROR_OUT_OF_MEMORY;
 
-        vsir_register_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
+        vsir_operand_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
         ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
         ins->dst[0].reg.idx[0].offset = *tmp_idx;
         ins->dst[0].write_mask = VKD3DSP_WRITEMASK_0;
 
-        vsir_register_init(&ins->src[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
+        vsir_operand_init(&ins->src[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
         ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4;
         ins->src[0].reg.idx[0].offset = *tmp_idx;
         ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X);
-        vsir_register_init(&ins->src[1].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
+        vsir_operand_init(&ins->src[1].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
         ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4;
         ins->src[1].reg.idx[0].offset = *tmp_idx;
         ins->src[1].swizzle = vkd3d_shader_create_swizzle(j, j, j, j);
@@ -1644,7 +1795,7 @@ static enum vkd3d_result vsir_program_lower_texkill(struct vsir_program *program
         return VKD3D_ERROR_OUT_OF_MEMORY;
     ins->flags = VKD3D_SHADER_CONDITIONAL_OP_NZ;
 
-    vsir_register_init(&ins->src[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
+    vsir_operand_init(&ins->src[0].reg, VKD3DSPR_TEMP, VSIR_DATA_U32, 1);
     ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4;
     ins->src[0].reg.idx[0].offset = *tmp_idx;
     ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X);
@@ -1765,7 +1916,7 @@ static enum vkd3d_result vsir_program_lower_udiv(struct vsir_program *program,
 
     vsir_src_operand_init_ssa(&mov->src[0], src1_id, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
     vsir_src_operand_init_ssa(&mov->src[1], src1_id, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
-    vsir_register_init(&mov->src[2].reg, VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0);
+    vsir_operand_init(&mov->src[2].reg, VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0);
     mov->src[2].reg.dimension = udiv->src[1].reg.dimension;
     mov->src[2].reg.u.immconst_u32[0] = 1;
     mov->src[2].reg.u.immconst_u32[1] = 1;
@@ -1800,7 +1951,7 @@ static enum vkd3d_result vsir_program_lower_udiv(struct vsir_program *program,
         vsir_src_operand_init_ssa(&ins->src[0], src1_id, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
         vsir_src_operand_init_ssa(&ins->src[1], program->ssa_count,
                 udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
-        vsir_register_init(&ins->src[2].reg, VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0);
+        vsir_operand_init(&ins->src[2].reg, VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0);
         ins->src[2].reg.dimension = udiv->src[1].reg.dimension;
         ins->src[2].reg.u.immconst_u32[0] = UINT_MAX;
         ins->src[2].reg.u.immconst_u32[1] = UINT_MAX;
@@ -1834,7 +1985,7 @@ static enum vkd3d_result vsir_program_lower_udiv(struct vsir_program *program,
         vsir_src_operand_init_ssa(&ins->src[0], src1_id, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
         vsir_src_operand_init_ssa(&ins->src[1], program->ssa_count,
                 udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
-        vsir_register_init(&ins->src[2].reg, VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0);
+        vsir_operand_init(&ins->src[2].reg, VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0);
         ins->src[2].reg.dimension = udiv->src[1].reg.dimension;
         ins->src[2].reg.u.immconst_u32[0] = UINT_MAX;
         ins->src[2].reg.u.immconst_u32[1] = UINT_MAX;
@@ -2069,17 +2220,68 @@ static enum vkd3d_result vsir_program_lower_texcrd(struct vsir_program *program,
         struct vkd3d_shader_instruction *ins, struct vkd3d_shader_message_context *message_context)
 {
     /* texcrd DST, t# -> mov DST, t# */
+    ins->opcode = VSIR_OP_MOV;
+    return VKD3D_OK;
+}
 
-    if (ins->src[0].modifiers)
-    {
-        vkd3d_shader_error(message_context, &ins->location, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED,
-                "Aborting due to not yet implemented feature: texcrd source modifier.");
-        return VKD3D_ERROR_NOT_IMPLEMENTED;
-    }
+static enum vkd3d_result vsir_program_lower_texdepth(struct vsir_program *program,
+        struct vsir_program_iterator *texdepth, struct vkd3d_shader_message_context *message_context)
+{
+    struct vkd3d_shader_instruction *ins = vsir_program_iterator_current(texdepth);
+    const struct vkd3d_shader_location location = ins->location;
+    uint32_t ssa_eq, ssa_div, ssa_sat;
+    struct vsir_program_iterator it;
 
-    ins->opcode = VSIR_OP_MOV;
+    /* texdepth r5
+     *      ->
+     * eq srEQ, r5.y, 0.0
+     * div srDIV, r5.x, r5.y
+     * saturate srSAT, srDIV
+     * movc oDepth, srEQ, 1.0, srSAT */
+
+    if (!(ins = vsir_program_iterator_insert_before(texdepth, &it, 3)))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+    if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_EQO, 1, 2))
+        goto fail;
+    ssa_eq = program->ssa_count++;
+    vsir_dst_operand_init_ssa_bool(&ins->dst[0], ssa_eq);
+    vsir_src_operand_init_temp_f32v4(&ins->src[0], 5);
+    ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(Y, Y, Y, Y);
+    vsir_src_operand_init_const_f32(&ins->src[1], 0.0f);
+
+    ins = vsir_program_iterator_next(&it);
+    if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_DIV, 1, 2))
+        goto fail;
+    ssa_div = program->ssa_count++;
+    vsir_dst_operand_init_ssa_f32(&ins->dst[0], ssa_div);
+    vsir_src_operand_init_temp_f32v4(&ins->src[0], 5);
+    ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X);
+    vsir_src_operand_init_temp_f32v4(&ins->src[1], 5);
+    ins->src[1].swizzle = VKD3D_SHADER_SWIZZLE(Y, Y, Y, Y);
+
+    ins = vsir_program_iterator_next(&it);
+    if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_SATURATE, 1, 1))
+        goto fail;
+    ssa_sat = program->ssa_count++;
+    vsir_dst_operand_init_ssa_f32(&ins->dst[0], ssa_sat);
+    vsir_src_operand_init_ssa_f32(&ins->src[0], ssa_div);
+
+    ins = vsir_program_iterator_next(&it);
+    if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_MOVC, 1, 3))
+        goto fail;
+    vsir_dst_operand_init(&ins->dst[0], VKD3DSPR_DEPTHOUT, VSIR_DATA_F32, 0);
+    ins->dst[0].reg.dimension = VSIR_DIMENSION_SCALAR;
+    vsir_src_operand_init_ssa_bool(&ins->src[0], ssa_eq);
+    vsir_src_operand_init_const_f32(&ins->src[1], 1.0f);
+    vsir_src_operand_init_ssa_f32(&ins->src[2], ssa_sat);
 
+    bitmap_set(program->io_dcls, VKD3DSPR_DEPTHOUT);
     return VKD3D_OK;
+
+fail:
+    vsir_program_iterator_nop_range(&it, texdepth, &location);
+
+    return VKD3D_ERROR_OUT_OF_MEMORY;
 }
 
 static enum vkd3d_result vsir_program_lower_texld_sm1(struct vsir_program *program,
@@ -2091,13 +2293,6 @@ static enum vkd3d_result vsir_program_lower_texld_sm1(struct vsir_program *progr
 
     /* texld DST, t# -> sample DST, t#, resource#, sampler# */
 
-    if (ins->src[0].modifiers)
-    {
-        vkd3d_shader_error(message_context, &ins->location, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED,
-                "Aborting due to not yet implemented feature: texld source modifier.");
-        return VKD3D_ERROR_NOT_IMPLEMENTED;
-    }
-
     if (!(srcs = vsir_program_get_src_operands(program, 4)))
         return VKD3D_ERROR_OUT_OF_MEMORY;
 
@@ -2323,12 +2518,44 @@ static enum vkd3d_result vsir_program_lower_tex(struct vsir_program *program,
 {
     struct vkd3d_shader_instruction *ins = vsir_program_iterator_current(it);
     const struct vkd3d_shader_location location = ins->location;
+    unsigned int idxN = ins->dst[0].reg.idx[0].offset, idxM;
     const struct vkd3d_shader_descriptor_info1 *sampler;
-    unsigned int idx = ins->dst[0].reg.idx[0].offset;
     struct vsir_src_operand *srcs;
+    uint32_t coords_swizzle;
 
-    /* tex t# -> sample t#, t#, resource#, sampler#
-     * Note that the t# destination will subsequently be turned into a temp. */
+    /* tex tN            -> sample tN, tN, resourceN, samplerN
+     * texreg2ar tN, tM  -> sample tN, tM.wx, resourceN, samplerN
+     * texreg2gb tN, tM  -> sample tN, tM.yz, resourceN, samplerN
+     * texreg2rgb tN, tM -> sample tN, tM.xyz, resourceN, samplerN
+     *
+     * Note that the tN destination will subsequently be turned into a temp.
+     */
+
+    switch (ins->opcode)
+    {
+        case VSIR_OP_TEX:
+            idxM = idxN;
+            coords_swizzle = VKD3D_SHADER_SWIZZLE(X, Y, Z, W);
+            break;
+
+        case VSIR_OP_TEXREG2AR:
+            idxM = ins->src[0].reg.idx[0].offset;
+            coords_swizzle = VKD3D_SHADER_SWIZZLE(W, X, X, X);
+            break;
+
+        case VSIR_OP_TEXREG2GB:
+            idxM = ins->src[0].reg.idx[0].offset;
+            coords_swizzle = VKD3D_SHADER_SWIZZLE(Y, Z, X, X);
+            break;
+
+        case VSIR_OP_TEXREG2RGB:
+            idxM = ins->src[0].reg.idx[0].offset;
+            coords_swizzle = VKD3D_SHADER_SWIZZLE(X, Y, Z, X);
+            break;
+
+        default:
+            vkd3d_unreachable();
+    }
 
     /* We run before I/O normalization. */
     VKD3D_ASSERT(program->normalisation_level < VSIR_NORMALISED_SM6);
@@ -2336,12 +2563,12 @@ static enum vkd3d_result vsir_program_lower_tex(struct vsir_program *program,
     if (!(srcs = vsir_program_get_src_operands(program, 4)))
         return VKD3D_ERROR_OUT_OF_MEMORY;
 
-    if (is_texture_projected(program, message_context, idx))
+    if (ins->opcode == VSIR_OP_TEX && is_texture_projected(program, message_context, idxN))
     {
         struct vsir_dst_operand *dst = ins->dst;
         uint32_t coords = program->ssa_count++;
 
-        /* div sr0, t#, t#.w */
+        /* div sr0, tN, tN.w */
 
         if (!vsir_program_iterator_insert_after(it, 1))
             return VKD3D_ERROR_OUT_OF_MEMORY;
@@ -2351,7 +2578,7 @@ static enum vkd3d_result vsir_program_lower_tex(struct vsir_program *program,
             return VKD3D_ERROR_OUT_OF_MEMORY;
         vsir_dst_operand_init_ssa_f32v4(&ins->dst[0], coords);
         vsir_src_operand_init(&ins->src[0], VKD3DSPR_TEXTURE, VSIR_DATA_F32, 1);
-        ins->src[0].reg.idx[0].offset = idx;
+        ins->src[0].reg.idx[0].offset = idxM;
         ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4;
         ins->src[0].swizzle = VKD3D_SHADER_NO_SWIZZLE;
         ins->src[1] = ins->src[0];
@@ -2366,16 +2593,16 @@ static enum vkd3d_result vsir_program_lower_tex(struct vsir_program *program,
     else
     {
         vsir_src_operand_init(&srcs[0], VKD3DSPR_TEXTURE, VSIR_DATA_F32, 1);
-        srcs[0].reg.idx[0].offset = idx;
+        srcs[0].reg.idx[0].offset = idxM;
         srcs[0].reg.dimension = VSIR_DIMENSION_VEC4;
-        srcs[0].swizzle = VKD3D_SHADER_NO_SWIZZLE;
+        srcs[0].swizzle = coords_swizzle;
     }
 
-    vsir_src_operand_init_resource(&srcs[1], idx, idx);
-    vsir_src_operand_init_sampler(&srcs[2], idx, idx);
+    vsir_src_operand_init_resource(&srcs[1], idxN, idxN);
+    vsir_src_operand_init_sampler(&srcs[2], idxN, idxN);
 
-    sampler = vkd3d_shader_find_descriptor(&program->descriptors, VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER, idx);
-    if (sampler->flags & VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_SAMPLER_COMPARISON_MODE)
+    sampler = vkd3d_shader_find_descriptor(&program->descriptors, VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER, idxN);
+    if (ins->opcode == VSIR_OP_TEX && sampler->flags & VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_SAMPLER_COMPARISON_MODE)
     {
         ins->opcode = VSIR_OP_SAMPLE_C;
         ins->src = srcs;
@@ -2448,6 +2675,9 @@ static struct vkd3d_shader_instruction *generate_bump_coords(struct vsir_program
     ins->src[0].swizzle = vsir_combine_swizzles(perturbation->swizzle, VKD3D_SHADER_SWIZZLE(X, X, X, X));
     vsir_src_operand_init_parameter_vec4(&ins->src[1], VKD3D_SHADER_PARAMETER_NAME_BUMP_MATRIX_0 + idx, VSIR_DATA_F32);
     ins->src[2] = *coords;
+    /* Copy rel_addrs in copy of *perturbation, to avoid multiple owners. */
+    if (!vsir_operand_clone_indirect_indices(&ins->src[0].reg, program))
+        return false;
 
     ins = vsir_program_iterator_next(it);
     if (!vsir_instruction_init_with_params(program, ins, loc, VSIR_OP_MAD, 1, 3))
@@ -2460,6 +2690,9 @@ static struct vkd3d_shader_instruction *generate_bump_coords(struct vsir_program
     ins->src[1].swizzle = VKD3D_SHADER_SWIZZLE(Z, W, W, W);
     vsir_src_operand_init_ssa_f32v4(&ins->src[2], ssa_temp);
     ins->src[2].swizzle = VKD3D_SHADER_SWIZZLE(X, Y, Y, Y);
+    /* Copy rel_addrs in copy of *perturbation, to avoid multiple owners. */
+    if (!vsir_operand_clone_indirect_indices(&ins->src[0].reg, program))
+        return false;
 
     return ins;
 }
@@ -2534,8 +2767,8 @@ fail:
     return VKD3D_ERROR_OUT_OF_MEMORY;
 }
 
-static bool vsir_register_replace_loop_register_with_temp(struct vsir_program *program,
-        struct vkd3d_shader_register *reg, unsigned int idxtemp_idx, unsigned int temp_id)
+static bool vsir_operand_replace_loop_register_with_temp(struct vsir_operand *reg,
+        struct vsir_program *program, unsigned int idxtemp_idx, unsigned int temp_id)
 {
     if (reg->type == VKD3DSPR_LOOP)
     {
@@ -2548,7 +2781,7 @@ static bool vsir_register_replace_loop_register_with_temp(struct vsir_program *p
 
     if (reg->idx[0].rel_addr && reg->idx[0].rel_addr->reg.type == VKD3DSPR_LOOP)
     {
-        if (!shader_register_clone_relative_addresses(reg, program))
+        if (!vsir_operand_clone_indirect_indices(reg, program))
             return false;
         if (reg->type == VKD3DSPR_INPUT)
         {
@@ -2671,13 +2904,13 @@ static enum vkd3d_result vsir_program_lower_loop(struct vsir_program *program,
 
         for (i = 0; i < ins->dst_count; ++i)
         {
-            if (!vsir_register_replace_loop_register_with_temp(program, &ins->dst[i].reg, idxtmp_idx, val_id))
+            if (!vsir_operand_replace_loop_register_with_temp(&ins->dst[i].reg, program, idxtmp_idx, val_id))
                 return VKD3D_ERROR_OUT_OF_MEMORY;
         }
 
         for (i = 0; i < ins->src_count; ++i)
         {
-            if (!vsir_register_replace_loop_register_with_temp(program, &ins->src[i].reg, idxtmp_idx, val_id))
+            if (!vsir_operand_replace_loop_register_with_temp(&ins->src[i].reg, program, idxtmp_idx, val_id))
                 return VKD3D_ERROR_OUT_OF_MEMORY;
         }
     }
@@ -2803,6 +3036,9 @@ static enum vkd3d_result vsir_program_lower_texbem(struct vsir_program *program,
                 VKD3D_SHADER_PARAMETER_NAME_BUMP_LUMINANCE_SCALE_0 + idx, VSIR_DATA_F32);
         vsir_src_operand_init_parameter(&ins->src[2],
                 VKD3D_SHADER_PARAMETER_NAME_BUMP_LUMINANCE_OFFSET_0 + idx, VSIR_DATA_F32);
+        /* Copy rel_addrs in additional use of SRC0, to avoid multiple owners. */
+        if (!vsir_operand_clone_indirect_indices(&ins->src[0].reg, program))
+            return VKD3D_ERROR_OUT_OF_MEMORY;
 
         ins = vsir_program_iterator_next(it);
         if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_MUL, 1, 2))
@@ -2927,10 +3163,23 @@ static enum vkd3d_result vsir_program_lower_d3dbc_instructions(struct vsir_progr
                 ret = vsir_program_lower_lrp(program, &it);
                 break;
 
+            case VSIR_OP_M3x2:
+            case VSIR_OP_M3x3:
+            case VSIR_OP_M3x4:
+            case VSIR_OP_M4x3:
+            case VSIR_OP_M4x4:
+                ret = vsir_program_lower_mnxn(program, &it);
+                break;
+
             case VSIR_OP_NRM:
                 ret = vsir_program_lower_nrm(program, &it);
                 break;
 
+            case VSIR_OP_PHASE:
+                vkd3d_shader_instruction_make_nop(ins);
+                ret = VKD3D_OK;
+                break;
+
             case VSIR_OP_POW:
                 ret = vsir_program_lower_pow(program, &it);
                 break;
@@ -2953,11 +3202,18 @@ static enum vkd3d_result vsir_program_lower_d3dbc_instructions(struct vsir_progr
                 ret = vsir_program_lower_texcrd(program, ins, message_context);
                 break;
 
+            case VSIR_OP_TEXDEPTH:
+                ret = vsir_program_lower_texdepth(program, &it, message_context);
+                break;
+
             case VSIR_OP_TEXKILL:
                 ret = vsir_program_lower_texkill(program, &it, &tmp_idx);
                 break;
 
             case VSIR_OP_TEX:
+            case VSIR_OP_TEXREG2AR:
+            case VSIR_OP_TEXREG2GB:
+            case VSIR_OP_TEXREG2RGB:
                 ret = vsir_program_lower_tex(program, &it, message_context);
                 break;
 
@@ -2978,7 +3234,6 @@ static enum vkd3d_result vsir_program_lower_d3dbc_instructions(struct vsir_progr
                 ret = vsir_program_lower_texldl(program, ins);
                 break;
 
-            case VSIR_OP_TEXDEPTH:
             case VSIR_OP_TEXDP3:
             case VSIR_OP_TEXDP3TEX:
             case VSIR_OP_TEXM3x2PAD:
@@ -2988,9 +3243,6 @@ static enum vkd3d_result vsir_program_lower_d3dbc_instructions(struct vsir_progr
             case VSIR_OP_TEXM3x3SPEC:
             case VSIR_OP_TEXM3x3TEX:
             case VSIR_OP_TEXM3x3VSPEC:
-            case VSIR_OP_TEXREG2AR:
-            case VSIR_OP_TEXREG2GB:
-            case VSIR_OP_TEXREG2RGB:
                 vkd3d_shader_error(ctx->message_context, &ins->location, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED,
                         "Aborting due to unimplemented feature: Combined sampler instruction \"%s\" (%#x).",
                         vsir_opcode_get_name(ins->opcode, "<unknown>"), ins->opcode);
@@ -3116,7 +3368,7 @@ fail:
     return VKD3D_ERROR_OUT_OF_MEMORY;
 }
 
-static bool is_input_register_with_loop_relative_addressing(struct vkd3d_shader_register *reg)
+static bool is_input_register_with_loop_relative_addressing(struct vsir_operand *reg)
 {
     if (reg->type != VKD3DSPR_INPUT)
         return false;
@@ -3202,6 +3454,38 @@ static enum vkd3d_result vsir_program_lower_modifiers(struct vsir_program *progr
                     new_opcodes[1] = VSIR_OP_NEG;
                     break;
 
+                case VKD3DSPSM_DW:
+                case VKD3DSPSM_DZ:
+                    if (!(new_ins = vsir_program_iterator_insert_before(&it, &new_it, 1)))
+                        return VKD3D_ERROR_OUT_OF_MEMORY;
+                    ins = vsir_program_iterator_current(&it);
+
+                    if (!vsir_instruction_init_with_params(program, new_ins, &ins->location, VSIR_OP_DIV, 1, 2))
+                    {
+                        vkd3d_shader_instruction_make_nop(new_ins);
+                        return VKD3D_ERROR_OUT_OF_MEMORY;
+                    }
+
+                    vsir_dst_operand_init_ssa_f32v4(&new_ins->dst[0], program->ssa_count);
+                    new_ins->src[0] = *src;
+                    new_ins->src[0].swizzle = VKD3D_SHADER_NO_SWIZZLE;
+                    new_ins->src[0].modifiers = VKD3DSPSM_NONE;
+                    new_ins->src[1] = new_ins->src[0];
+                    if (!vsir_operand_clone_indirect_indices(&new_ins->src[1].reg, program))
+                    {
+                        vkd3d_shader_instruction_make_nop(new_ins);
+                        return VKD3D_ERROR_OUT_OF_MEMORY;
+                    }
+                    if (src->modifiers == VKD3DSPSM_DW)
+                        new_ins->src[1].swizzle = VKD3D_SHADER_SWIZZLE(W, W, W, W);
+                    else
+                        new_ins->src[1].swizzle = VKD3D_SHADER_SWIZZLE(Z, Z, Z, Z);
+
+                    vsir_src_operand_init_ssa_f32v4(src, program->ssa_count);
+
+                    ++program->ssa_count;
+                    continue;
+
                 default:
                     vkd3d_shader_error(ctx->message_context, &ins->location, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED,
                             "Aborting due to unimplemented feature: Source modifier %#x.", src->modifiers);
@@ -3481,6 +3765,69 @@ static enum vkd3d_result vsir_program_normalise_ps1_output(struct vsir_program *
     return VKD3D_OK;
 }
 
+/* In vs_3_0, the point size output is a normal output register declared with dcl_psize.
+ * Remap writes to that register to the point size system value register. */
+static enum vkd3d_result vsir_program_normalise_vs3_point_size_output(struct vsir_program *program,
+        struct vsir_transformation_context *ctx)
+{
+    struct vsir_program_iterator it = vsir_program_iterator(&program->instructions);
+    struct vkd3d_shader_instruction *ins;
+    const struct signature_element *e;
+
+    if (!program->has_point_size)
+        return VKD3D_OK;
+
+    e = vsir_signature_find_element_by_name(&program->output_signature, "PSIZE", 0);
+    VKD3D_ASSERT(e);
+
+    for (ins = vsir_program_iterator_head(&it); ins; ins = vsir_program_iterator_next(&it))
+    {
+        if (vsir_instruction_is_dcl(ins))
+            continue;
+
+        for (size_t j = 0; j < ins->dst_count; ++j)
+        {
+            struct vsir_operand *reg = &ins->dst[j].reg;
+
+            /* Remap the PSIZE output register to the point size sysval register. */
+            if (reg->type == VKD3DSPR_OUTPUT && reg->idx[0].offset == e->register_index)
+            {
+                reg->type = VKD3DSPR_RASTOUT;
+                reg->idx[0].offset = VSIR_RASTOUT_POINT_SIZE;
+                reg->idx_count = 1;
+            }
+        }
+    }
+
+    return VKD3D_OK;
+}
+
+static enum vkd3d_result vsir_program_clamp_vs2_color_output(struct vsir_program *program,
+        struct vsir_transformation_context *ctx)
+{
+    struct vsir_program_iterator it = vsir_program_iterator(&program->instructions);
+    struct vkd3d_shader_instruction *ins;
+
+    if (program->shader_version.type != VKD3D_SHADER_TYPE_VERTEX)
+        return VKD3D_OK;
+
+    for (ins = vsir_program_iterator_head(&it); ins; ins = vsir_program_iterator_next(&it))
+    {
+        if (vsir_instruction_is_dcl(ins))
+            continue;
+
+        for (size_t j = 0; j < ins->dst_count; ++j)
+        {
+            struct vsir_operand *reg = &ins->dst[j].reg;
+
+            if (reg->type == VKD3DSPR_ATTROUT)
+                ins->dst[j].modifiers |= VKD3DSPDM_SATURATE;
+        }
+    }
+
+    return VKD3D_OK;
+}
+
 static struct signature_element *add_signature_element(struct shader_signature *signature,
         const char *semantic_name, uint32_t semantic_index, uint32_t mask, uint32_t register_index,
         enum vkd3d_shader_interpolation_mode interpolation_mode)
@@ -3756,6 +4103,8 @@ static enum vkd3d_result vsir_program_remap_output_signature(struct vsir_program
                 map->input_register_index, map->input_mask);
         e = &signature->elements[signature->element_count++];
         memset(e, 0, sizeof(*e));
+        if (!(e->semantic_name = vkd3d_strdup("$unused")))
+            return VKD3D_ERROR_OUT_OF_MEMORY;
         e->sysval_semantic = VKD3D_SHADER_SV_NONE;
         e->component_type = VKD3D_SHADER_COMPONENT_FLOAT;
         e->register_count = 1;
@@ -3819,11 +4168,11 @@ struct hull_flattener
 };
 
 static void flattener_fixup_ssa_register(struct hull_flattener *normaliser,
-        struct vkd3d_shader_register *reg, unsigned int instance_id)
+        struct vsir_operand *reg, unsigned int instance_id)
 {
     unsigned int id;
 
-    if (!register_is_ssa(reg))
+    if (!vsir_operand_is_ssa(reg))
         return;
 
     /* No need to alter the first copy, they are already not conflicting. */
@@ -3838,7 +4187,7 @@ static void flattener_fixup_ssa_register(struct hull_flattener *normaliser,
 }
 
 static void flattener_fixup_register_indices(struct hull_flattener *normaliser,
-        struct vkd3d_shader_register *reg, unsigned int instance_id)
+        struct vsir_operand *reg, unsigned int instance_id)
 {
     unsigned int i;
 
@@ -3849,7 +4198,7 @@ static void flattener_fixup_register_indices(struct hull_flattener *normaliser,
         if (reg->idx[i].rel_addr)
         {
             flattener_fixup_ssa_register(normaliser, &reg->idx[i].rel_addr->reg, instance_id);
-            if (shader_register_is_phase_instance_id(&reg->idx[i].rel_addr->reg))
+            if (vsir_operand_is_phase_instance_id(&reg->idx[i].rel_addr->reg))
             {
                 reg->idx[i].rel_addr = NULL;
                 reg->idx[i].offset += instance_id;
@@ -3861,15 +4210,15 @@ static void flattener_fixup_register_indices(struct hull_flattener *normaliser,
 static void flattener_fixup_registers(struct hull_flattener *normaliser,
         struct vkd3d_shader_instruction *ins, unsigned int instance_id)
 {
-    struct vkd3d_shader_register *reg;
+    struct vsir_operand *reg;
     unsigned int i;
 
     for (i = 0; i < ins->src_count; ++i)
     {
         reg = &ins->src[i].reg;
-        if (shader_register_is_phase_instance_id(reg))
+        if (vsir_operand_is_phase_instance_id(reg))
         {
-            vsir_register_init(reg, VKD3DSPR_IMMCONST, reg->data_type, 0);
+            vsir_operand_init(reg, VKD3DSPR_IMMCONST, reg->data_type, 0);
             reg->u.immconst_u32[0] = instance_id;
             continue;
         }
@@ -4025,7 +4374,6 @@ struct control_point_normaliser
 {
     struct vsir_program *program;
     enum vkd3d_shader_opcode phase;
-    struct vsir_src_operand *outpointid_param;
 };
 
 struct vsir_src_operand *vsir_program_create_outpointid_param(struct vsir_program *program)
@@ -4033,19 +4381,22 @@ struct vsir_src_operand *vsir_program_create_outpointid_param(struct vsir_progra
     struct vsir_src_operand *rel_addr;
 
     if (!(rel_addr = vsir_program_get_src_operands(program, 1)))
+    {
+        ERR("Failed to allocate source operand.\n");
         return NULL;
+    }
 
-    vsir_register_init(&rel_addr->reg, VKD3DSPR_OUTPOINTID, VSIR_DATA_U32, 0);
+    vsir_operand_init(&rel_addr->reg, VKD3DSPR_OUTPOINTID, VSIR_DATA_U32, 0);
     rel_addr->swizzle = 0;
     rel_addr->modifiers = 0;
 
     return rel_addr;
 }
 
-static void vsir_dst_operand_normalise_outpointid(struct vsir_dst_operand *dst,
+static bool vsir_dst_operand_normalise_outpointid(struct vsir_dst_operand *dst,
         struct control_point_normaliser *normaliser)
 {
-    struct vkd3d_shader_register *reg = &dst->reg;
+    struct vsir_operand *reg = &dst->reg;
 
     if (vsir_opcode_is_control_point_phase(normaliser->phase) && reg->type == VKD3DSPR_OUTPUT)
     {
@@ -4054,9 +4405,11 @@ static void vsir_dst_operand_normalise_outpointid(struct vsir_dst_operand *dst,
         reg->idx[1] = reg->idx[0];
         /* The control point id param is implicit here. Avoid later complications by inserting it. */
         reg->idx[0].offset = 0;
-        reg->idx[0].rel_addr = normaliser->outpointid_param;
+        if (!(reg->idx[0].rel_addr = vsir_program_create_outpointid_param(normaliser->program)))
+            return false;
         ++reg->idx_count;
     }
+    return true;
 }
 
 static enum vkd3d_result control_point_normaliser_emit_hs_input(struct control_point_normaliser *normaliser,
@@ -4094,18 +4447,18 @@ static enum vkd3d_result control_point_normaliser_emit_hs_input(struct control_p
             return VKD3D_ERROR_OUT_OF_MEMORY;
         }
 
-        VKD3D_ASSERT(normaliser->outpointid_param);
-
         vsir_dst_operand_init_io(&ins->dst[0], VKD3DSPR_OUTPUT, e, 2);
         ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
         ins->dst[0].reg.idx[0].offset = 0;
-        ins->dst[0].reg.idx[0].rel_addr = normaliser->outpointid_param;
+        if (!(ins->dst[0].reg.idx[0].rel_addr = vsir_program_create_outpointid_param(normaliser->program)))
+            return VKD3D_ERROR_OUT_OF_MEMORY;
         ins->dst[0].reg.idx[1].offset = e->register_index;
 
         vsir_src_operand_init_io(&ins->src[0], VKD3DSPR_INPUT, e, 2);
         ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4;
         ins->src[0].reg.idx[0].offset = 0;
-        ins->src[0].reg.idx[0].rel_addr = normaliser->outpointid_param;
+        if (!(ins->src[0].reg.idx[0].rel_addr = vsir_program_create_outpointid_param(normaliser->program)))
+            return VKD3D_ERROR_OUT_OF_MEMORY;
         ins->src[0].reg.idx[1].offset = e->register_index;
 
         ins = vsir_program_iterator_next(dst_it);
@@ -4135,11 +4488,6 @@ static enum vkd3d_result instruction_array_normalise_hull_shader_control_point_i
         return VKD3D_OK;
     }
 
-    if (!(normaliser.outpointid_param = vsir_program_create_outpointid_param(program)))
-    {
-        ERR("Failed to allocate src param.\n");
-        return VKD3D_ERROR_OUT_OF_MEMORY;
-    }
     normaliser.program = program;
     it = vsir_program_iterator(&normaliser.program->instructions);
     normaliser.phase = VSIR_OP_INVALID;
@@ -4158,7 +4506,8 @@ static enum vkd3d_result instruction_array_normalise_hull_shader_control_point_i
                     break;
                 for (j = 0; j < ins->dst_count; ++j)
                 {
-                    vsir_dst_operand_normalise_outpointid(&ins->dst[j], &normaliser);
+                    if (!vsir_dst_operand_normalise_outpointid(&ins->dst[j], &normaliser))
+                        return VKD3D_ERROR_OUT_OF_MEMORY;
                 }
                 break;
         }
@@ -4224,6 +4573,7 @@ struct io_normaliser
     struct io_normaliser_register_data input_range_map[MAX_REG_OUTPUT];
     struct io_normaliser_register_data output_range_map[MAX_REG_OUTPUT];
     struct io_normaliser_register_data pc_range_map[MAX_REG_OUTPUT];
+    uint32_t vpos_ssa, vface_ssa;
 
     bool use_vocp;
 };
@@ -4281,7 +4631,6 @@ static enum vkd3d_result range_map_set_register_range(struct io_normaliser *norm
     {
         if (range_map[register_idx].component[component_idx].register_count == UINT8_MAX)
         {
-            WARN("Conflicting index ranges.\n");
             vkd3d_shader_error(normaliser->message_context, NULL,
                     VKD3D_SHADER_ERROR_VSIR_INVALID_SIGNATURE, "Conflicting index ranges.");
             return VKD3D_ERROR_INVALID_SHADER;
@@ -4309,7 +4658,6 @@ static enum vkd3d_result range_map_set_register_range(struct io_normaliser *norm
              * The latter is validated in the TPF reader. */
             if (range_map[r].component[c].register_count && is_dcl_indexrange)
             {
-                WARN("Conflicting index ranges.\n");
                 vkd3d_shader_error(normaliser->message_context, NULL,
                         VKD3D_SHADER_ERROR_VSIR_INVALID_SIGNATURE, "Conflicting index ranges.");
                 return VKD3D_ERROR_INVALID_SHADER;
@@ -4327,7 +4675,7 @@ static enum vkd3d_result io_normaliser_add_index_range(struct io_normaliser *nor
         const struct vkd3d_shader_instruction *ins)
 {
     const struct vkd3d_shader_index_range *range = &ins->declaration.index_range;
-    const struct vkd3d_shader_register *reg = &range->dst.reg;
+    const struct vsir_operand *reg = &range->dst.reg;
     struct io_normaliser_register_data *range_map;
     const struct shader_signature *signature;
     uint32_t mask, used_mask;
@@ -4597,7 +4945,7 @@ out:
     return ret;
 }
 
-static unsigned int shader_register_normalise_arrayed_addressing(struct vkd3d_shader_register *reg,
+static unsigned int vsir_operand_normalise_arrayed_addressing(struct vsir_operand *reg,
         unsigned int id_idx, unsigned int register_index)
 {
     VKD3D_ASSERT(id_idx < ARRAY_SIZE(reg->idx) - 1);
@@ -4621,8 +4969,8 @@ static bool vsir_dst_operand_io_normalise(struct vsir_dst_operand *dst,
         struct io_normaliser *normaliser, struct vkd3d_shader_instruction *ins)
 {
     unsigned int id_idx, reg_idx, write_mask, element_idx;
-    struct vkd3d_shader_register *reg = &dst->reg;
     const struct shader_signature *signature;
+    struct vsir_operand *reg = &dst->reg;
     const struct signature_element *e;
 
     write_mask = dst->write_mask;
@@ -4701,7 +5049,7 @@ static bool vsir_dst_operand_io_normalise(struct vsir_dst_operand *dst,
     e = &signature->elements[element_idx];
 
     if (vsir_signature_element_is_array(e, normaliser->normalisation_flags))
-        id_idx = shader_register_normalise_arrayed_addressing(reg, id_idx, e->register_index);
+        id_idx = vsir_operand_normalise_arrayed_addressing(reg, id_idx, e->register_index);
 
     /* Replace the register index with the signature element index */
     reg->idx[id_idx].offset = element_idx;
@@ -4714,8 +5062,8 @@ static void vsir_src_operand_io_normalise(struct vsir_src_operand *src,
         struct io_normaliser *normaliser, struct vkd3d_shader_instruction *ins)
 {
     unsigned int i, id_idx, reg_idx, write_mask, element_idx, component_idx;
-    struct vkd3d_shader_register *reg = &src->reg;
     const struct shader_signature *signature;
+    struct vsir_operand *reg = &src->reg;
     const struct signature_element *e;
 
     /* Input/output registers from one phase can be used as inputs in
@@ -4767,6 +5115,19 @@ static void vsir_src_operand_io_normalise(struct vsir_src_operand *src,
             signature = normaliser->input_signature;
             break;
 
+        case VKD3DSPR_MISCTYPE:
+            if (reg->idx[0].offset == 0)
+            {
+                reg->type = VKD3DSPR_SSA;
+                reg->idx[0].offset = normaliser->vpos_ssa;
+            }
+            else if (reg->idx[0].offset == 1)
+            {
+                reg->type = VKD3DSPR_SSA;
+                reg->idx[0].offset = normaliser->vface_ssa;
+            }
+            return;
+
         default:
             return;
     }
@@ -4784,7 +5145,7 @@ static void vsir_src_operand_io_normalise(struct vsir_src_operand *src,
 
     e = &signature->elements[element_idx];
     if (vsir_signature_element_is_array(e, normaliser->normalisation_flags))
-        id_idx = shader_register_normalise_arrayed_addressing(reg, id_idx, e->register_index);
+        id_idx = vsir_operand_normalise_arrayed_addressing(reg, id_idx, e->register_index);
     reg->idx[id_idx].offset = element_idx;
     reg->idx_count = id_idx + 1;
 
@@ -4798,6 +5159,66 @@ static void vsir_src_operand_io_normalise(struct vsir_src_operand *src,
     }
 }
 
+static enum vkd3d_result io_normaliser_init_misctype(struct vsir_program_iterator *it,
+        struct vsir_program *program, struct io_normaliser *normaliser)
+{
+    struct vkd3d_shader_location location = vsir_program_iterator_current(it)->location;
+    const struct signature_element *element;
+    struct vkd3d_shader_instruction *ins;
+    unsigned int signature_idx;
+
+    if (normaliser->shader_type != VKD3D_SHADER_TYPE_PIXEL || normaliser->major != 3)
+        return VKD3D_OK;
+
+    if (vsir_signature_find_sysval(normaliser->input_signature, VKD3D_SHADER_SV_POSITION, 0, &signature_idx))
+    {
+        element = &normaliser->input_signature->elements[signature_idx];
+
+        /* sm4 (and therefore vsir) SV_Position returns the pixel center,
+         * i.e. coordinates ending in .5, but vPos should return integer
+         * coordinates. Take the floor. */
+
+        if (!(ins = vsir_program_iterator_insert_before_and_move(it, 1)))
+            return VKD3D_ERROR_OUT_OF_MEMORY;
+        normaliser->vpos_ssa = program->ssa_count++;
+        if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_ROUND_Z, 1, 1))
+        {
+            vsir_instruction_init(ins, &location, VSIR_OP_NOP);
+            return VKD3D_ERROR_OUT_OF_MEMORY;
+        }
+        vsir_dst_operand_init_ssa_f32v4(&ins->dst[0], normaliser->vpos_ssa);
+        vsir_src_operand_init_io(&ins->src[0], VKD3DSPR_INPUT, element, 1);
+        ins->src[0].reg.idx[0].offset = signature_idx;
+        vsir_program_iterator_next(it);
+    }
+
+    if (vsir_signature_find_sysval(normaliser->input_signature, VKD3D_SHADER_SV_IS_FRONT_FACE, 0, &signature_idx))
+    {
+        element = &normaliser->input_signature->elements[signature_idx];
+
+        /* sm4 (and therefore vsir) SV_IsFrontFace returns ~0u for front
+         * and 0 for back. vFace returns 1.0 for front and -1.0 for back. */
+
+        if (!(ins = vsir_program_iterator_insert_before_and_move(it, 1)))
+            return VKD3D_ERROR_OUT_OF_MEMORY;
+        normaliser->vface_ssa = program->ssa_count++;
+        if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_MOVC, 1, 3))
+        {
+            vsir_instruction_init(ins, &location, VSIR_OP_NOP);
+            return VKD3D_ERROR_OUT_OF_MEMORY;
+        }
+        vsir_dst_operand_init_ssa_f32v4(&ins->dst[0], normaliser->vface_ssa);
+        vsir_src_operand_init_io(&ins->src[0], VKD3DSPR_INPUT, element, 1);
+        ins->src[0].reg.idx[0].offset = signature_idx;
+        ins->src[0].reg.data_type = VSIR_DATA_U32;
+        vsir_src_operand_init_const_f32(&ins->src[1], 1.0f);
+        vsir_src_operand_init_const_f32(&ins->src[2], -1.0f);
+        vsir_program_iterator_next(it);
+    }
+
+    return VKD3D_OK;
+}
+
 static void shader_instruction_normalise_io_params(struct vkd3d_shader_instruction *ins,
         struct io_normaliser *normaliser)
 {
@@ -4834,6 +5255,7 @@ static enum vkd3d_result vsir_program_normalise_io_registers(struct vsir_program
     struct vsir_program_iterator it = vsir_program_iterator(&program->instructions);
     struct io_normaliser normaliser = {ctx->message_context, VKD3D_OK};
     struct vkd3d_shader_instruction *ins;
+    bool misctype_initialized = false;
     enum vkd3d_result ret;
 
     VKD3D_ASSERT(program->normalisation_level == VSIR_NORMALISED_HULL_CONTROL_POINT_IO);
@@ -4845,6 +5267,7 @@ static enum vkd3d_result vsir_program_normalise_io_registers(struct vsir_program
     normaliser.output_signature = &program->output_signature;
     normaliser.patch_constant_signature = &program->patch_constant_signature;
     normaliser.normalisation_flags = &program->normalisation_flags;
+    normaliser.vpos_ssa = normaliser.vface_ssa = ~0u;
 
     for (ins = vsir_program_iterator_head(&it); ins; ins = vsir_program_iterator_next(&it))
     {
@@ -4861,6 +5284,13 @@ static enum vkd3d_result vsir_program_normalise_io_registers(struct vsir_program
                 normaliser.phase = ins->opcode;
                 break;
             default:
+                if (!misctype_initialized && ins->opcode != VSIR_OP_NOP)
+                {
+                    if ((ret = io_normaliser_init_misctype(&it, program, &normaliser)))
+                        return ret;
+                    misctype_initialized = true;
+                    ins = vsir_program_iterator_current(&it);
+                }
                 break;
         }
     }
@@ -4896,7 +5326,7 @@ struct flat_constants_normaliser
     size_t def_count, defs_capacity;
 };
 
-static bool get_flat_constant_register_type(const struct vkd3d_shader_register *reg,
+static bool get_flat_constant_register_type(const struct vsir_operand *reg,
         enum vkd3d_shader_d3dbc_constant_register *set, uint32_t *index, struct vsir_src_operand **rel_addr)
 {
     static const struct
@@ -5626,7 +6056,6 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte
 
                 if (src->swizzle != VKD3D_SHADER_SWIZZLE(X, X, X, X))
                 {
-                    WARN("Unexpected src swizzle %#x.\n", src->swizzle);
                     vkd3d_shader_error(message_context, &instruction->location,
                             VKD3D_SHADER_ERROR_VSIR_INVALID_SWIZZLE,
                             "The swizzle for a switch case value is not scalar X.");
@@ -5975,8 +6404,8 @@ static bool ssas_to_temps_alloc_init(struct ssas_to_temps_alloc *alloc, unsigned
 
 /* This is idempotent: it can be safely applied more than once on the
  * same register. */
-static void materialize_ssas_to_temps_process_reg(struct vsir_program *program, struct ssas_to_temps_alloc *alloc,
-        struct vkd3d_shader_register *reg)
+static void materialize_ssas_to_temps_process_reg(struct vsir_program *program,
+        struct ssas_to_temps_alloc *alloc, struct vsir_operand *reg)
 {
     unsigned int i;
 
@@ -6596,7 +7025,7 @@ static void vsir_cfg_dump_dot(struct vsir_cfg *cfg)
                 break;
 
             case VSIR_OP_BRANCH:
-                shape = vsir_register_is_label(&end->src[0].reg) ? "ellipse" : "box";
+                shape = vsir_operand_is_label(&end->src[0].reg) ? "ellipse" : "box";
                 break;
 
             default:
@@ -6798,7 +7227,7 @@ static enum vkd3d_result vsir_cfg_init(struct vsir_cfg *cfg, struct vsir_program
                 break;
 
             case VSIR_OP_BRANCH:
-                if (vsir_register_is_label(&end->src[0].reg))
+                if (vsir_operand_is_label(&end->src[0].reg))
                 {
                     if ((ret = vsir_cfg_add_edge(cfg, block, &end->src[0])) < 0)
                         goto fail;
@@ -6994,7 +7423,6 @@ static enum vkd3d_result vsir_cfg_compute_loops(struct vsir_cfg *cfg)
             {
                 struct vkd3d_shader_instruction *begin = vsir_program_iterator_current(&header->begin);
 
-                FIXME("Block %u is header to more than one loop, this is not implemented.\n", header->label);
                 vkd3d_shader_error(cfg->message_context, &begin->location, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED,
                         "Block %u is header to more than one loop, this is not implemented.", header->label);
                 return VKD3D_ERROR_NOT_IMPLEMENTED;
@@ -7079,7 +7507,6 @@ static enum vkd3d_result vsir_cfg_sort_nodes(struct vsir_cfg *cfg)
         {
             struct vkd3d_shader_instruction *begin = vsir_program_iterator_current(&block->begin);
 
-            WARN("Unexpected entry point %u.\n", block->label);
             vkd3d_shader_error(cfg->message_context, &begin->location, VKD3D_SHADER_ERROR_VSIR_INVALID_CONTROL_FLOW,
                     "Block %u is unreachable from the entry point.", block->label);
             ret = VKD3D_ERROR_INVALID_SHADER;
@@ -7091,7 +7518,6 @@ static enum vkd3d_result vsir_cfg_sort_nodes(struct vsir_cfg *cfg)
     {
         struct vkd3d_shader_instruction *begin = vsir_program_iterator_current(&cfg->entry->begin);
 
-        WARN("Entry point has %u incoming forward edges.\n", in_degrees[cfg->entry->label - 1]);
         vkd3d_shader_error(cfg->message_context, &begin->location, VKD3D_SHADER_ERROR_VSIR_INVALID_CONTROL_FLOW,
                 "The entry point block has %u incoming forward edges.", in_degrees[cfg->entry->label - 1]);
         ret = VKD3D_ERROR_INVALID_SHADER;
@@ -7197,7 +7623,6 @@ static enum vkd3d_result vsir_cfg_sort_nodes(struct vsir_cfg *cfg)
         struct vkd3d_shader_instruction *begin = vsir_program_iterator_current(&cfg->entry->begin);
 
         /* There is a cycle of forward edges. */
-        WARN("The control flow graph is not reducible.\n");
         vkd3d_shader_error(cfg->message_context, &begin->location, VKD3D_SHADER_ERROR_VSIR_INVALID_CONTROL_FLOW,
                 "The control flow graph is not reducible.");
         ret = VKD3D_ERROR_INVALID_SHADER;
@@ -7491,7 +7916,7 @@ static enum vkd3d_result vsir_cfg_build_structured_program(struct vsir_cfg *cfg)
                 struct vsir_cfg_edge_action action_true, action_false;
                 bool invert_condition = false;
 
-                if (vsir_register_is_label(&end->src[0].reg))
+                if (vsir_operand_is_label(&end->src[0].reg))
                 {
                     unsigned int target = label_from_src_operand(&end->src[0]);
                     struct vsir_block *successor = &cfg->blocks[target - 1];
@@ -8405,12 +8830,12 @@ fail:
     return ret;
 }
 
-static void register_map_undominated_use(struct vkd3d_shader_register *reg, struct ssas_to_temps_alloc *alloc,
+static void register_map_undominated_use(struct vsir_operand *reg, struct ssas_to_temps_alloc *alloc,
         struct vsir_block *block, struct vsir_block **origin_blocks)
 {
     unsigned int i;
 
-    if (register_is_ssa(reg))
+    if (vsir_operand_is_ssa(reg))
     {
         i = reg->idx[0].offset;
         if (alloc->table[i] == UINT_MAX && !vsir_block_dominates(origin_blocks[i], block))
@@ -8461,7 +8886,7 @@ static enum vkd3d_result vsir_cfg_materialize_undominated_ssas_to_temps(struct v
         {
             for (j = 0; j < ins->dst_count; ++j)
             {
-                if (register_is_ssa(&ins->dst[j].reg))
+                if (vsir_operand_is_ssa(&ins->dst[j].reg))
                     origin_blocks[ins->dst[j].reg.idx[0].offset] = block;
             }
         }
@@ -9083,7 +9508,6 @@ static enum vkd3d_result sysval_array_normaliser_add_output_copy(
         struct sysval_array_normaliser *normaliser, struct vsir_program_iterator *it)
 {
     struct vsir_program *program = normaliser->ctx->program;
-    struct vsir_src_operand *outpointid_param = NULL;
     unsigned int output_component_count = 0;
     struct vkd3d_shader_instruction *mov;
     struct signature_element *element;
@@ -9099,13 +9523,6 @@ static enum vkd3d_result sysval_array_normaliser_add_output_copy(
     element = &program->output_signature.elements[normaliser->element_idx];
     loc = vsir_program_iterator_current(it)->location;
 
-    if (program->shader_version.type == VKD3D_SHADER_TYPE_HULL
-            && !(outpointid_param = vsir_program_create_outpointid_param(program)))
-    {
-        ERR("Failed to allocate outpointid param.\n");
-        return VKD3D_ERROR_OUT_OF_MEMORY;
-    }
-
     for (unsigned int q = 0; q < normaliser->reg_count; ++q)
     {
         for (unsigned int k = 0; k < VKD3D_VEC4_SIZE; ++k)
@@ -9128,11 +9545,12 @@ static enum vkd3d_result sysval_array_normaliser_add_output_copy(
             dst->reg.idx[1].offset = normaliser->element_idx;
             dst->reg.dimension = VSIR_DIMENSION_VEC4;
             dst->write_mask = VKD3DSP_WRITEMASK_0;
-            if (outpointid_param)
+            if (program->shader_version.type == VKD3D_SHADER_TYPE_HULL)
             {
                 dst->reg.idx_count = 3;
                 dst->reg.idx[2] = dst->reg.idx[1];
-                dst->reg.idx[1].rel_addr = outpointid_param;
+                if (!(dst->reg.idx[1].rel_addr = vsir_program_create_outpointid_param(program)))
+                    return VKD3D_ERROR_OUT_OF_MEMORY;
                 dst->reg.idx[1].offset = 0;
             }
 
@@ -9275,9 +9693,9 @@ static enum vkd3d_result sysval_array_normaliser_dcl_indexable_temp(
     return VKD3D_OK;
 }
 
-static bool vsir_program_validate_outpointid_control_point_index(const struct vkd3d_shader_register *reg)
+static bool vsir_program_validate_outpointid_control_point_index(const struct vsir_operand *reg)
 {
-    const struct vkd3d_shader_register_index *index;
+    const struct vsir_register_index *index;
 
     if (reg->idx_count < 2)
         return false;
@@ -9337,10 +9755,10 @@ static bool vsir_program_validate_outpointid_control_point_index(const struct vk
  * relevant signature element 'e'.
  */
 static enum vkd3d_result sysval_array_normaliser_map_register(struct sysval_array_normaliser *normaliser,
-        struct vsir_program_iterator *it, struct vkd3d_shader_register *reg, unsigned int *src_swizzle)
+        struct vsir_program_iterator *it, struct vsir_operand *reg, unsigned int *src_swizzle)
 {
-    struct vkd3d_shader_register_index i_idx = {0}, p_idx = {0};
     struct vsir_program *program = normaliser->ctx->program;
+    struct vsir_register_index i_idx = {0}, p_idx = {0};
     unsigned int element_index, control_point_count;
     struct vkd3d_shader_instruction *ssa_ins;
     struct shader_signature *signature;
@@ -9415,7 +9833,7 @@ static enum vkd3d_result sysval_array_normaliser_map_register(struct sysval_arra
         if (!vsir_instruction_init_with_params(program, ssa_ins, &loc, VSIR_OP_IMUL_LOW, 1, 2))
             return VKD3D_ERROR_OUT_OF_MEMORY;
 
-        vsir_register_init(&ssa_ins->dst[0].reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1);
+        vsir_operand_init(&ssa_ins->dst[0].reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1);
         ssa_ins->dst[0].reg.idx[0].offset = program->ssa_count++;
         ssa_ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
         ssa_ins->dst[0].write_mask = VKD3DSP_WRITEMASK_0;
@@ -9428,11 +9846,11 @@ static enum vkd3d_result sysval_array_normaliser_map_register(struct sysval_arra
             if (!vsir_instruction_init_with_params(program, ssa_ins, &loc, VSIR_OP_ADD, 1, 2))
                 return VKD3D_ERROR_OUT_OF_MEMORY;
 
-            vsir_register_init(&ssa_ins->dst[0].reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1);
+            vsir_operand_init(&ssa_ins->dst[0].reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1);
             ssa_ins->dst[0].reg.idx[0].offset = program->ssa_count++;
             ssa_ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
             ssa_ins->dst[0].write_mask = VKD3DSP_WRITEMASK_0;
-            vsir_register_init(&ssa_ins->src[0].reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1);
+            vsir_operand_init(&ssa_ins->src[0].reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1);
             ssa_ins->src[0].reg.idx[0].offset = program->ssa_count - 2;
             ssa_ins->src[1] = *i_idx.rel_addr;
         }
@@ -9442,7 +9860,7 @@ static enum vkd3d_result sysval_array_normaliser_map_register(struct sysval_arra
         reg->idx[1].offset = normaliser->reg_count * p_idx.offset + i_idx.offset + q;
         if (!(reg->idx[1].rel_addr = vsir_program_get_src_operands(program, 1)))
             return VKD3D_ERROR_OUT_OF_MEMORY;
-        vsir_register_init(&reg->idx[1].rel_addr->reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1);
+        vsir_operand_init(&reg->idx[1].rel_addr->reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1);
         reg->idx[1].rel_addr->reg.idx[0].offset = program->ssa_count - 1;
         reg->idx[1].rel_addr->reg.dimension = VSIR_DIMENSION_VEC4;
         reg->idx[1].rel_addr->swizzle = VKD3D_SHADER_SWIZZLE_X;
@@ -9451,10 +9869,16 @@ static enum vkd3d_result sysval_array_normaliser_map_register(struct sysval_arra
     else
     {
         reg->idx[1].offset = normaliser->reg_count * p_idx.offset + i_idx.offset + q;
-        reg->idx[1].rel_addr = i_idx.rel_addr;
-    }
-
-    if (src_swizzle)
+        reg->idx[1].rel_addr = NULL;
+        if (i_idx.rel_addr)
+        {
+            if (!(reg->idx[1].rel_addr = vsir_program_get_src_operands(program, 1)))
+                return VKD3D_ERROR_OUT_OF_MEMORY;
+            *reg->idx[1].rel_addr = *i_idx.rel_addr;
+        }
+    }
+
+    if (src_swizzle)
         *src_swizzle = vsir_combine_swizzles(vsir_swizzle_from_writemask(element->mask), *src_swizzle);
 
     return VKD3D_OK;
@@ -9490,7 +9914,7 @@ static enum vkd3d_result sysval_array_normaliser_map_instruction(
     return VKD3D_OK;
 }
 
-static void shader_register_remove_signature_element(struct vkd3d_shader_register *reg,
+static void vsir_operand_remove_signature_element(struct vsir_operand *reg,
         enum vkd3d_shader_register_type type, unsigned int index)
 {
     unsigned int current_idx;
@@ -9498,7 +9922,7 @@ static void shader_register_remove_signature_element(struct vkd3d_shader_registe
     for (unsigned int i = 0; i < reg->idx_count; ++i)
     {
         if (reg->idx[i].rel_addr)
-            shader_register_remove_signature_element(&reg->idx[i].rel_addr->reg, type, index);
+            vsir_operand_remove_signature_element(&reg->idx[i].rel_addr->reg, type, index);
     }
 
     if (reg->type != type)
@@ -9538,9 +9962,9 @@ static void vsir_program_remove_signature_element(struct vsir_program *program,
         if (vsir_instruction_is_dcl(ins))
             continue;
         for (unsigned int i = 0; i < ins->dst_count; ++i)
-            shader_register_remove_signature_element(&ins->dst[i].reg, type, index);
+            vsir_operand_remove_signature_element(&ins->dst[i].reg, type, index);
         for (unsigned int i = 0; i < ins->src_count; ++i)
-            shader_register_remove_signature_element(&ins->src[i].reg, type, index);
+            vsir_operand_remove_signature_element(&ins->src[i].reg, type, index);
     }
 
     vsir_signature_element_cleanup(&signature->elements[index]);
@@ -10848,12 +11272,15 @@ struct liveness_tracker
         bool written;
         bool fixed_mask;
         uint8_t mask;
+        unsigned int interior_loop_start, interior_loop_depth;
         unsigned int first_write, last_access, last_read;
     } *ssa_regs, *temp_regs;
 };
 
 static void liveness_track_src(struct liveness_tracker *tracker, struct vsir_src_operand *src, unsigned int index)
 {
+    struct liveness_tracker_reg *reg;
+
     for (unsigned int k = 0; k < src->reg.idx_count; ++k)
     {
         if (src->reg.idx[k].rel_addr)
@@ -10861,14 +11288,21 @@ static void liveness_track_src(struct liveness_tracker *tracker, struct vsir_src
     }
 
     if (src->reg.type == VKD3DSPR_SSA)
-    {
-        tracker->ssa_regs[src->reg.idx[0].offset].last_read = index;
-        tracker->ssa_regs[src->reg.idx[0].offset].last_access = index;
-    }
+        reg = &tracker->ssa_regs[src->reg.idx[0].offset];
     else if (src->reg.type == VKD3DSPR_TEMP)
+        reg = &tracker->temp_regs[src->reg.idx[0].offset];
+    else
+        return;
+
+    reg->last_read = index;
+    reg->last_access = index;
+    if (!reg->written)
     {
-        tracker->temp_regs[src->reg.idx[0].offset].last_read = index;
-        tracker->temp_regs[src->reg.idx[0].offset].last_access = index;
+        WARN("Register %s%u used uninitialised in instruction %u. Recording implicit write.\n",
+                vsir_register_type_get_name(src->reg.type, NULL), src->reg.idx[0].offset, index);
+        reg->first_write = index;
+        reg->last_access = index;
+        reg->written = true;
     }
 }
 
@@ -10993,49 +11427,70 @@ static enum vkd3d_result track_liveness(struct vsir_program *program, struct liv
         {
             if (!loop_depth++)
                 loop_start = i;
+
+            for (unsigned int j = 0; j < program->ssa_count; ++j)
+            {
+                struct liveness_tracker_reg *reg = &tracker->ssa_regs[j];
+
+                /* Only track the topmost loop started after this SSA was
+                 * written. We need to extend the liveness to its end
+                 * if it is in fact read at any point during that loop. */
+                if (reg->written && !reg->interior_loop_depth)
+                {
+                    reg->interior_loop_start = i;
+                    reg->interior_loop_depth = loop_depth;
+                }
+            }
         }
         else if (ins->opcode == VSIR_OP_ENDLOOP || ins->opcode == VSIR_OP_ENDREP)
         {
-            if (!--loop_depth)
+            --loop_depth;
+
+            /* SSA registers should always be written before they are read.
+             * Moreover, if they are written in a loop, all reads must be
+             * inside the same loop. However, reads can be inside a loop
+             * that follows the write.
+             *
+             * In this case, the register must not be scratched between
+             * iterations of the loop, so we need to extend the liveness
+             * to the end of the loop. We track the beginning of each
+             * immediate child loop that follows the write, and if the last
+             * access was after that loop began, extend it to the loop end.
+             *
+             * For temps, we don't have these restrictions. A temp can be
+             * written at the end of the loop and read earlier in the same
+             * loop, and the value has to be preserved, so we need to extend
+             * both the first write and last access. It may be possible to
+             * be more sophisticated, but many of the cases that matter are
+             * affected by other optimizations such as copy propagation
+             * anyway.
+             */
+            for (unsigned int j = 0; j < program->ssa_count; ++j)
             {
-                /* Go through the allocator, find anything that was touched
-                 * during the loop, and extend its liveness to the whole range
-                 * of the loop.
-                 * This isn't very sophisticated (e.g. we could try to avoid
-                 * this for registers first written inside a loop body and only
-                 * ever read inside one), but many of the cases that matter are
-                 * affected by other optimizations such as copy propagation
-                 * anyway.
-                 *
-                 * This is overkill for SSA registers. If an SSA register is
-                 * written in loop L and last read in L, we don't need to touch
-                 * its liveness. If it's last read in an inferior loop of L, we
-                 * only need to extend its last-read to the end of L. (And it
-                 * should be illegal for an SSA value to be read in a block
-                 * containing L.)
-                 * We don't try to perform this optimization yet, in the name of
-                 * maximal simplicity. */
-                for (unsigned int j = 0; j < program->ssa_count; ++j)
-                {
-                    struct liveness_tracker_reg *reg = &tracker->ssa_regs[j];
+                struct liveness_tracker_reg *reg = &tracker->ssa_regs[j];
 
-                    if (reg->first_write > loop_start)
-                        reg->first_write = loop_start;
-                    if (reg->last_access < i)
+                if (reg->written && reg->interior_loop_depth == loop_depth + 1)
+                {
+                    if (reg->last_access > reg->interior_loop_start)
                         reg->last_access = i;
-                    if (reg->last_read < i)
+                    if (reg->last_read > reg->interior_loop_start)
                         reg->last_read = i;
+
+                    reg->interior_loop_depth = 0;
                 }
+            }
 
+            if (!loop_depth)
+            {
                 for (unsigned int j = 0; j < program->temp_count; ++j)
                 {
                     struct liveness_tracker_reg *reg = &tracker->temp_regs[j];
 
                     if (reg->first_write > loop_start)
                         reg->first_write = loop_start;
-                    if (reg->last_access < i)
+                    if (reg->last_access > loop_start && reg->last_access < i)
                         reg->last_access = i;
-                    if (reg->last_read < i)
+                    if (reg->last_read > loop_start && reg->last_read < i)
                         reg->last_read = i;
                 }
             }
@@ -11288,8 +11743,10 @@ static void temp_allocator_open_register(struct temp_allocator *allocator, struc
              * We currently only handle cases where the mask is zero-based and
              * contiguous, so we need to fill in the missing components to
              * ensure this. */
-            uint8_t mask = (1u << (vkd3d_log2i(liveness_reg->mask) + 1)) - 1;
+            uint8_t mask = 0;
 
+            if (liveness_reg->mask)
+                mask = (1u << (vkd3d_log2i(liveness_reg->mask) + 1)) - 1;
             if (vkd3d_popcount(available_mask) >= vkd3d_popcount(mask))
             {
                 if (mask != liveness_reg->mask)
@@ -11672,8 +12129,10 @@ struct validation_context
     } input_signature_data, output_signature_data, patch_constant_signature_data;
 };
 
-static void VKD3D_PRINTF_FUNC(3, 4) validator_error(struct validation_context *ctx,
-        enum vkd3d_shader_error error, const char *format, ...)
+#define validator_error(ctx, error, ...) \
+        validator_error_(ctx, error, __FUNCTION__, __VA_ARGS__)
+static void VKD3D_PRINTF_FUNC(4, 5) validator_error_(struct validation_context *ctx,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     struct vkd3d_string_buffer buf;
     va_list args;
@@ -11686,12 +12145,12 @@ static void VKD3D_PRINTF_FUNC(3, 4) validator_error(struct validation_context *c
 
     if (ctx->invalid_instruction_idx)
     {
-        vkd3d_shader_error(ctx->message_context, &ctx->location, error, "%s", buf.buffer);
+        vkd3d_shader_error_(ctx->message_context, &ctx->location, error, function, "%s", buf.buffer);
         WARN("VSIR validation error: %s\n", buf.buffer);
     }
     else
     {
-        vkd3d_shader_error(ctx->message_context, &ctx->location, error,
+        vkd3d_shader_error_(ctx->message_context, &ctx->location, error, function,
                 "instruction %zu: %s", ctx->instruction_idx + 1, buf.buffer);
         WARN("VSIR validation error: instruction %zu: %s\n", ctx->instruction_idx + 1, buf.buffer);
     }
@@ -11702,8 +12161,7 @@ static void VKD3D_PRINTF_FUNC(3, 4) validator_error(struct validation_context *c
         ctx->status = VKD3D_ERROR_INVALID_SHADER;
 }
 
-static void vsir_validate_register_without_indices(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg)
+static void vsir_validate_register_without_indices(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     if (reg->idx_count != 0)
         validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_INDEX_COUNT,
@@ -11919,7 +12377,7 @@ static const bool vsir_get_io_register_data(struct validation_context *ctx,
     }
 }
 
-static void vsir_validate_io_register(struct validation_context *ctx, const struct vkd3d_shader_register *reg)
+static void vsir_validate_io_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     unsigned int control_point_index, control_point_count;
     const struct shader_signature *signature;
@@ -12019,7 +12477,7 @@ static void vsir_validate_io_register(struct validation_context *ctx, const stru
                 reg->idx[control_point_index].offset, control_point_count, reg->type);
 }
 
-static void vsir_validate_texture_register(struct validation_context *ctx, const struct vkd3d_shader_register *reg)
+static void vsir_validate_texture_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     const struct vkd3d_shader_version *version = &ctx->program->shader_version;
     uint32_t idx;
@@ -12050,8 +12508,7 @@ static void vsir_validate_texture_register(struct validation_context *ctx, const
                 "Register t%u exceeds limits for version %u.%u.", idx, version->major, version->minor);
 }
 
-static void vsir_validate_temp_register(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg)
+static void vsir_validate_temp_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     struct validation_context_temp_data *data;
 
@@ -12101,8 +12558,7 @@ static void vsir_validate_temp_register(struct validation_context *ctx,
     }
 }
 
-static void vsir_validate_rastout_register(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg)
+static void vsir_validate_rastout_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     if (reg->idx_count != 1)
     {
@@ -12121,8 +12577,7 @@ static void vsir_validate_rastout_register(struct validation_context *ctx,
                 "Invalid offset for a RASTOUT register.");
 }
 
-static void vsir_validate_misctype_register(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg)
+static void vsir_validate_misctype_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     if (reg->idx_count != 1)
     {
@@ -12141,8 +12596,7 @@ static void vsir_validate_misctype_register(struct validation_context *ctx,
                 "Invalid offset for a MISCTYPE register.");
 }
 
-static void vsir_validate_label_register(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg)
+static void vsir_validate_label_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     if (reg->precision != VKD3D_SHADER_REGISTER_PRECISION_DEFAULT)
         validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_PRECISION,
@@ -12178,7 +12632,7 @@ static void vsir_validate_label_register(struct validation_context *ctx,
 }
 
 static void vsir_validate_descriptor_indices(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg, enum vkd3d_shader_descriptor_type type, const char *name)
+        const struct vsir_operand *reg, enum vkd3d_shader_descriptor_type type, const char *name)
 {
     const struct vkd3d_shader_descriptor_info1 *descriptor;
 
@@ -12204,8 +12658,7 @@ static void vsir_validate_descriptor_indices(struct validation_context *ctx,
                 descriptor->register_index + descriptor->count - 1, name, reg->idx[0].offset);
 }
 
-static void vsir_validate_constbuffer_register(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg)
+static void vsir_validate_constbuffer_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     if (reg->dimension != VSIR_DIMENSION_VEC4)
         validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_DIMENSION,
@@ -12221,8 +12674,7 @@ static void vsir_validate_constbuffer_register(struct validation_context *ctx,
     vsir_validate_descriptor_indices(ctx, reg, VKD3D_SHADER_DESCRIPTOR_TYPE_CBV, "cb");
 }
 
-static void vsir_validate_sampler_register(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg)
+static void vsir_validate_sampler_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     if (reg->precision != VKD3D_SHADER_REGISTER_PRECISION_DEFAULT)
         validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_PRECISION,
@@ -12248,8 +12700,7 @@ static void vsir_validate_sampler_register(struct validation_context *ctx,
     vsir_validate_descriptor_indices(ctx, reg, VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER, "s");
 }
 
-static void vsir_validate_resource_register(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg)
+static void vsir_validate_resource_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     if (reg->precision != VKD3D_SHADER_REGISTER_PRECISION_DEFAULT)
         validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_PRECISION,
@@ -12274,8 +12725,7 @@ static void vsir_validate_resource_register(struct validation_context *ctx,
     vsir_validate_descriptor_indices(ctx, reg, VKD3D_SHADER_DESCRIPTOR_TYPE_SRV, "t");
 }
 
-static void vsir_validate_uav_register(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg)
+static void vsir_validate_uav_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     if (reg->precision != VKD3D_SHADER_REGISTER_PRECISION_DEFAULT)
         validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_PRECISION,
@@ -12304,8 +12754,7 @@ static void vsir_validate_uav_register(struct validation_context *ctx,
     vsir_validate_descriptor_indices(ctx, reg, VKD3D_SHADER_DESCRIPTOR_TYPE_UAV, "u");
 }
 
-static void vsir_validate_ssa_register(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg)
+static void vsir_validate_ssa_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     struct validation_context_ssa_data *data;
 
@@ -12366,8 +12815,23 @@ static void vsir_validate_ssa_register(struct validation_context *ctx,
 
 static void vsir_validate_src_operand(struct validation_context *ctx, const struct vsir_src_operand *src);
 
-static void vsir_validate_register(struct validation_context *ctx,
-        const struct vkd3d_shader_register *reg)
+static bool vsir_rel_addr_reg_type_is_valid(enum vkd3d_shader_register_type type)
+{
+    switch (type)
+    {
+        case VKD3DSPR_TEMP:
+        case VKD3DSPR_SSA:
+        case VKD3DSPR_ADDR:
+        case VKD3DSPR_LOOP:
+        case VKD3DSPR_OUTPOINTID:
+            return true;
+
+        default:
+            return false;
+    }
+}
+
+static void vsir_validate_register(struct validation_context *ctx, const struct vsir_operand *reg)
 {
     static const struct register_validation_data
     {
@@ -12423,21 +12887,10 @@ static void vsir_validate_register(struct validation_context *ctx,
         {
             vsir_validate_src_operand(ctx, src);
 
-            switch (src->reg.type)
-            {
-                case VKD3DSPR_TEMP:
-                case VKD3DSPR_SSA:
-                case VKD3DSPR_ADDR:
-                case VKD3DSPR_LOOP:
-                case VKD3DSPR_OUTPOINTID:
-                    break;
-
-                default:
-                    validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
-                            "Invalid register type %#x for a relative address parameter.",
-                            src->reg.type);
-                    break;
-            }
+            if (!vsir_rel_addr_reg_type_is_valid(src->reg.type))
+                validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
+                        "Invalid register type %#x for a relative address parameter.",
+                        src->reg.type);
         }
     }
 
@@ -12922,28 +13375,42 @@ static const char * const signature_type_names[] =
 #define DS_BIT (1u << VKD3D_SHADER_TYPE_DOMAIN)
 #define CS_BIT (1u << VKD3D_SHADER_TYPE_COMPUTE)
 
+#define FLOAT_BIT (1u << VKD3D_SHADER_COMPONENT_FLOAT)
+#define INT_BIT (1u << VKD3D_SHADER_COMPONENT_INT)
+#define UINT_BIT (1u << VKD3D_SHADER_COMPONENT_UINT)
+#define FLOAT16_BIT (1u << VKD3D_SHADER_COMPONENT_FLOAT16)
+#define INT16_BIT (1u << VKD3D_SHADER_COMPONENT_INT16)
+#define UINT16_BIT (1u << VKD3D_SHADER_COMPONENT_UINT16)
+
 static const struct sysval_validation_data_element
 {
     unsigned int input;
     unsigned int output;
     unsigned int patch_constant;
-    enum vkd3d_shader_component_type data_type;
+    unsigned int data_mask;
     unsigned int component_count;
 }
 sysval_validation_data[] =
 {
-    [VKD3D_SHADER_SV_POSITION] = {PS_BIT | GS_BIT | HS_BIT | DS_BIT, VS_BIT | GS_BIT | HS_BIT | DS_BIT, 0,
-            VKD3D_SHADER_COMPONENT_FLOAT, 4},
-    [VKD3D_SHADER_SV_CLIP_DISTANCE] = {PS_BIT | GS_BIT | HS_BIT | DS_BIT, PS_BIT | VS_BIT | GS_BIT | HS_BIT | DS_BIT, 0,
-            VKD3D_SHADER_COMPONENT_FLOAT, 4},
-    [VKD3D_SHADER_SV_CULL_DISTANCE] = {PS_BIT | GS_BIT | HS_BIT | DS_BIT, PS_BIT | VS_BIT | GS_BIT | HS_BIT | DS_BIT, 0,
-            VKD3D_SHADER_COMPONENT_FLOAT, 4},
-    [VKD3D_SHADER_SV_TESS_FACTOR_QUADEDGE] = {0, 0, HS_BIT | DS_BIT, VKD3D_SHADER_COMPONENT_FLOAT, 1},
-    [VKD3D_SHADER_SV_TESS_FACTOR_QUADINT] = {0, 0, HS_BIT | DS_BIT, VKD3D_SHADER_COMPONENT_FLOAT, 1},
-    [VKD3D_SHADER_SV_TESS_FACTOR_TRIEDGE] = {0, 0, HS_BIT | DS_BIT, VKD3D_SHADER_COMPONENT_FLOAT, 1},
-    [VKD3D_SHADER_SV_TESS_FACTOR_TRIINT] = {0, 0, HS_BIT | DS_BIT, VKD3D_SHADER_COMPONENT_FLOAT, 1},
-    [VKD3D_SHADER_SV_TESS_FACTOR_LINEDET] = {0, 0, HS_BIT | DS_BIT, VKD3D_SHADER_COMPONENT_FLOAT, 1},
-    [VKD3D_SHADER_SV_TESS_FACTOR_LINEDEN] = {0, 0, HS_BIT | DS_BIT, VKD3D_SHADER_COMPONENT_FLOAT, 1},
+    [VKD3D_SHADER_SV_POSITION] = {PS_BIT | GS_BIT | HS_BIT | DS_BIT,
+            VS_BIT | GS_BIT | HS_BIT | DS_BIT, 0, FLOAT_BIT, 4},
+    [VKD3D_SHADER_SV_CLIP_DISTANCE] = {PS_BIT | GS_BIT | HS_BIT | DS_BIT,
+            PS_BIT | VS_BIT | GS_BIT | HS_BIT | DS_BIT, 0, FLOAT_BIT, 4},
+    [VKD3D_SHADER_SV_CULL_DISTANCE] = {PS_BIT | GS_BIT | HS_BIT | DS_BIT,
+            PS_BIT | VS_BIT | GS_BIT | HS_BIT | DS_BIT, 0, FLOAT_BIT, 4},
+    [VKD3D_SHADER_SV_RENDER_TARGET_ARRAY_INDEX] = {HS_BIT | DS_BIT | GS_BIT | PS_BIT,
+            VS_BIT | HS_BIT | DS_BIT | GS_BIT, 0, UINT_BIT, 1},
+    [VKD3D_SHADER_SV_VIEWPORT_ARRAY_INDEX] = {HS_BIT | DS_BIT | GS_BIT | PS_BIT,
+            VS_BIT | HS_BIT | DS_BIT | GS_BIT, 0, UINT_BIT, 1},
+    [VKD3D_SHADER_SV_VERTEX_ID] = {VS_BIT, 0, 0, UINT_BIT, 1},
+    [VKD3D_SHADER_SV_TARGET] = {0, PS_BIT, 0,
+            FLOAT_BIT | UINT_BIT | INT_BIT | FLOAT16_BIT | UINT16_BIT | INT16_BIT, 4},
+    [VKD3D_SHADER_SV_TESS_FACTOR_QUADEDGE] = {0, 0, HS_BIT | DS_BIT, FLOAT_BIT, 1},
+    [VKD3D_SHADER_SV_TESS_FACTOR_QUADINT] = {0, 0, HS_BIT | DS_BIT, FLOAT_BIT, 1},
+    [VKD3D_SHADER_SV_TESS_FACTOR_TRIEDGE] = {0, 0, HS_BIT | DS_BIT, FLOAT_BIT, 1},
+    [VKD3D_SHADER_SV_TESS_FACTOR_TRIINT] = {0, 0, HS_BIT | DS_BIT, FLOAT_BIT, 1},
+    [VKD3D_SHADER_SV_TESS_FACTOR_LINEDET] = {0, 0, HS_BIT | DS_BIT, FLOAT_BIT, 1},
+    [VKD3D_SHADER_SV_TESS_FACTOR_LINEDEN] = {0, 0, HS_BIT | DS_BIT, FLOAT_BIT, 1},
 };
 
 static void vsir_validate_signature_element(struct validation_context *ctx,
@@ -13135,7 +13602,7 @@ static void vsir_validate_signature_element(struct validation_context *ctx,
 
         if (data->component_count != 0)
         {
-            if (element->component_type != data->data_type)
+            if (!((1u << element->component_type) & data->data_mask))
                 validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_SIGNATURE,
                         "element %u of %s signature: Invalid data type %#x for system value semantic %#x.",
                         idx, signature_type_name, element->component_type, element->sysval_semantic);
@@ -13750,7 +14217,7 @@ static void vsir_validate_branch(struct validation_context *ctx, const struct vk
     if (!vsir_validate_src_min_count(ctx, instruction, 1))
         return;
 
-    if (vsir_register_is_label(&instruction->src[0].reg))
+    if (vsir_operand_is_label(&instruction->src[0].reg))
     {
         /* Unconditional branch: parameters are jump label,
          * optional merge label, optional continue label. */
@@ -13758,7 +14225,7 @@ static void vsir_validate_branch(struct validation_context *ctx, const struct vk
 
         for (i = 0; i < instruction->src_count; ++i)
         {
-            if (!vsir_register_is_label(&instruction->src[i].reg))
+            if (!vsir_operand_is_label(&instruction->src[i].reg))
                 validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
                         "Invalid register of type %#x in unconditional BRANCH instruction, expected LABEL.",
                         instruction->src[i].reg.type);
@@ -13774,7 +14241,7 @@ static void vsir_validate_branch(struct validation_context *ctx, const struct vk
 
         for (i = 1; i < instruction->src_count; ++i)
         {
-            if (!vsir_register_is_label(&instruction->src[i].reg))
+            if (!vsir_operand_is_label(&instruction->src[i].reg))
                 validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
                         "Invalid register of type %#x in conditional BRANCH instruction, expected LABEL.",
                         instruction->src[i].reg.type);
@@ -14358,7 +14825,7 @@ static void vsir_validate_itoi(struct validation_context *ctx, const struct vkd3
 static void vsir_validate_label(struct validation_context *ctx, const struct vkd3d_shader_instruction *instruction)
 {
     vsir_validate_cf_type(ctx, instruction, VSIR_CF_BLOCKS);
-    if (instruction->src_count >= 1 && !vsir_register_is_label(&instruction->src[0].reg))
+    if (instruction->src_count >= 1 && !vsir_operand_is_label(&instruction->src[0].reg))
         validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
                 "Invalid register of type %#x in a LABEL instruction, expected LABEL.",
                 instruction->src[0].reg.type);
@@ -14399,8 +14866,8 @@ static void vsir_validate_phi(struct validation_context *ctx, const struct vkd3d
         unsigned int value_idx = 2 * i;
         unsigned int label_idx = 2 * i + 1;
 
-        if (!register_is_constant_or_undef(&instruction->src[value_idx].reg)
-                && !register_is_ssa(&instruction->src[value_idx].reg))
+        if (!vsir_operand_is_constant_or_undef(&instruction->src[value_idx].reg)
+                && !vsir_operand_is_ssa(&instruction->src[value_idx].reg))
             validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
                     "Invalid value register for incoming %u of type %#x in PHI instruction, "
                     "expected SSA, IMMCONST or IMMCONST64.", i, instruction->src[value_idx].reg.type);
@@ -14410,7 +14877,7 @@ static void vsir_validate_phi(struct validation_context *ctx, const struct vkd3d
                     "Invalid value dimension %#x for incoming %u in PHI instruction, expected scalar.",
                     instruction->src[value_idx].reg.dimension, i);
 
-        if (!vsir_register_is_label(&instruction->src[label_idx].reg))
+        if (!vsir_operand_is_label(&instruction->src[label_idx].reg))
             validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
                     "Invalid label register for case %u of type %#x in PHI instruction, "
                     "expected LABEL.", i, instruction->src[value_idx].reg.type);
@@ -14419,7 +14886,7 @@ static void vsir_validate_phi(struct validation_context *ctx, const struct vkd3d
     if (instruction->dst_count < 1)
         return;
 
-    if (!register_is_ssa(&instruction->dst[0].reg))
+    if (!vsir_operand_is_ssa(&instruction->dst[0].reg))
         validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
                 "Invalid destination of type %#x in PHI instruction, expected SSA.",
                 instruction->dst[0].reg.type);
@@ -14524,12 +14991,12 @@ static void vsir_validate_switch_monolithic(struct validation_context *ctx,
                 "Invalid source count %zu for a monolithic SWITCH instruction, it must be an odd number.",
                 instruction->src_count);
 
-    if (!vsir_register_is_label(&instruction->src[1].reg))
+    if (!vsir_operand_is_label(&instruction->src[1].reg))
         validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
                 "Invalid default label register of type %#x in monolithic SWITCH instruction, expected LABEL.",
                 instruction->src[1].reg.type);
 
-    if (!vsir_register_is_label(&instruction->src[2].reg))
+    if (!vsir_operand_is_label(&instruction->src[2].reg))
         validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
                 "Invalid merge label register of type %#x in monolithic SWITCH instruction, expected LABEL.",
                 instruction->src[2].reg.type);
@@ -14541,12 +15008,12 @@ static void vsir_validate_switch_monolithic(struct validation_context *ctx,
         unsigned int value_idx = 3 + 2 * i;
         unsigned int label_idx = 3 + 2 * i + 1;
 
-        if (!register_is_constant(&instruction->src[value_idx].reg))
+        if (!vsir_operand_is_constant(&instruction->src[value_idx].reg))
             validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
                     "Invalid value register for case %u of type %#x in monolithic SWITCH instruction, "
                     "expected IMMCONST or IMMCONST64.", i, instruction->src[value_idx].reg.type);
 
-        if (!vsir_register_is_label(&instruction->src[label_idx].reg))
+        if (!vsir_operand_is_label(&instruction->src[label_idx].reg))
             validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
                     "Invalid label register for case %u of type %#x in monolithic SWITCH instruction, "
                     "expected LABEL.", i, instruction->src[value_idx].reg.type);
@@ -14555,6 +15022,35 @@ static void vsir_validate_switch_monolithic(struct validation_context *ctx,
     ctx->inside_block = false;
 }
 
+static void vsir_validate_texdepth(struct validation_context *ctx,
+        const struct vkd3d_shader_instruction *instruction)
+{
+    const struct vkd3d_shader_version *version = &ctx->program->shader_version;
+
+    if (version->type != VKD3D_SHADER_TYPE_PIXEL)
+        validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
+                "TEXDEPTH cannot be used in shader type %#x.", version->type);
+
+    if (version->major != 1 || version->minor != 4)
+        validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_OPCODE,
+                "TEXDEPTH cannot be used in version %u.%u.", version->major, version->minor);
+
+    if (instruction->dst[0].write_mask != VKD3DSP_WRITEMASK_ALL)
+        validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_WRITE_MASK,
+                "Invalid TEXDEPTH write mask %#x.", instruction->dst[0].write_mask);
+
+    if (instruction->dst[0].reg.type != VKD3DSPR_TEMP)
+        validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_REGISTER_TYPE,
+                "Invalid TEXDEPTH register type %#x.", instruction->dst[0].reg.type);
+
+    if (instruction->dst[0].reg.idx[0].offset != 5)
+        validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_INDEX,
+                "Invalid TEXDEPTH register index %u.", instruction->dst[0].reg.idx[0].offset);
+
+    /* Strictly not an elementwise operation, but we expect the dest to be float. */
+    vsir_validate_float_elementwise_operation(ctx, instruction);
+}
+
 static void vsir_validate_ushr(struct validation_context *ctx,
         const struct vkd3d_shader_instruction *instruction)
 {
@@ -14690,6 +15186,7 @@ static const struct vsir_validator_instruction_desc vsir_validator_instructions[
     [VSIR_OP_SATURATE] =                         {1,   1, vsir_validate_float_or_double_elementwise_operation},
     [VSIR_OP_SWITCH] =                           {0,   1, vsir_validate_switch},
     [VSIR_OP_SWITCH_MONOLITHIC] =                {0, ~0u, vsir_validate_switch_monolithic},
+    [VSIR_OP_TEXDEPTH] =                         {1,   0, vsir_validate_texdepth},
     [VSIR_OP_USHR] =                             {1,   2, vsir_validate_ushr},
 };
 
@@ -14773,6 +15270,73 @@ static void vsir_validate_instruction(struct validation_context *ctx,
     }
 }
 
+static void vsir_program_validate_update_src_operand_owner(struct validation_context *ctx,
+        struct vsir_src_operand *src, bool reset, size_t owner_index)
+{
+    if (!src)
+        return;
+
+    if (reset)
+    {
+        src->owner_index = 0;
+        return;
+    }
+
+    if (src->owner_index)
+    {
+        if (src->owner_index == owner_index)
+            validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_MULTIPLE_SRC_OPERAND_OWNERS,
+                    "Source operand is used more than once by this instruction.");
+        else
+            validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_MULTIPLE_SRC_OPERAND_OWNERS,
+                    "Source operand is used by more than one instruction, first one is %zu.", src->owner_index);
+        return;
+    }
+    src->owner_index = owner_index;
+}
+
+static enum vkd3d_result vsir_program_validate_src_operands_owners(
+        struct validation_context *ctx, struct vsir_program *program)
+{
+    struct vsir_program_iterator it = vsir_program_iterator(&program->instructions);
+    struct vkd3d_shader_instruction *ins;
+    struct vsir_src_operand *src;
+    struct vsir_dst_operand *dst;
+
+    for (unsigned int t = 0; t < 2; ++t)
+    {
+        ctx->invalid_instruction_idx = false;
+        ctx->instruction_idx = 0;
+        for (ins = vsir_program_iterator_head(&it); ins; ins = vsir_program_iterator_next(&it))
+        {
+            ctx->location = ins->location;
+            for (unsigned int i = 0; i < ins->src_count; ++i)
+            {
+                src = &ins->src[i];
+                vsir_program_validate_update_src_operand_owner(ctx, src, !t, ctx->instruction_idx + 1);
+                for (unsigned int k = 0; k < src->reg.idx_count; ++k)
+                {
+                    vsir_program_validate_update_src_operand_owner(ctx,
+                            src->reg.idx[k].rel_addr, !t, ctx->instruction_idx + 1);
+                }
+            }
+            for (unsigned int i = 0; i < ins->dst_count; ++i)
+            {
+                dst = &ins->dst[i];
+                for (unsigned int k = 0; k < dst->reg.idx_count; ++k)
+                {
+                    vsir_program_validate_update_src_operand_owner(ctx,
+                            dst->reg.idx[k].rel_addr, !t, ctx->instruction_idx + 1);
+                }
+            }
+            ++ctx->instruction_idx;
+        }
+    }
+    ctx->invalid_instruction_idx = true;
+
+    return ctx->status;
+}
+
 enum vkd3d_result vsir_program_validate(struct vsir_program *program, uint64_t config_flags,
         const char *source_name, struct vkd3d_shader_message_context *message_context)
 {
@@ -14899,8 +15463,9 @@ enum vkd3d_result vsir_program_validate(struct vsir_program *program, uint64_t c
     if (!(ctx.ssas = vkd3d_calloc(ctx.program->ssa_count, sizeof(*ctx.ssas))))
         goto fail;
 
-    ctx.invalid_instruction_idx = false;
+    vsir_program_validate_src_operands_owners(&ctx, program);
 
+    ctx.invalid_instruction_idx = false;
     ctx.instruction_idx = 0;
     for (ins = vsir_program_iterator_head(&it); ins && ctx.status != VKD3D_ERROR_OUT_OF_MEMORY;
             ins = vsir_program_iterator_next(&it))
@@ -15426,7 +15991,6 @@ static bool is_read_only(const struct vsir_program *program, enum vkd3d_shader_r
         /* Not applicable since they're not numeric or can't be sources. */
         case VKD3DSPR_ATTROUT:
         case VKD3DSPR_COLOROUT:
-        case VKD3DSPR_COMBINED_SAMPLER:
         case VKD3DSPR_COUNT:
         case VKD3DSPR_DEPTHOUT:
         case VKD3DSPR_DEPTHOUTGE:
@@ -15442,13 +16006,12 @@ static bool is_read_only(const struct vsir_program *program, enum vkd3d_shader_r
         case VKD3DSPR_PREDICATE:
         case VKD3DSPR_RASTERIZER:
         case VKD3DSPR_RASTOUT:
-        case VKD3DSPR_RESOURCE:
-        case VKD3DSPR_SAMPLER:
         case VKD3DSPR_STREAM:
         case VKD3DSPR_TEXCRDOUT:
         case VKD3DSPR_UAV:
             return false;
 
+        case VKD3DSPR_COMBINED_SAMPLER:
         case VKD3DSPR_CONST:
         case VKD3DSPR_CONSTBOOL:
         case VKD3DSPR_CONSTBUFFER:
@@ -15471,7 +16034,9 @@ static bool is_read_only(const struct vsir_program *program, enum vkd3d_shader_r
         case VKD3DSPR_PATCHCONST:
         case VKD3DSPR_POINT_COORD:
         case VKD3DSPR_PRIMID:
+        case VKD3DSPR_RESOURCE:
         case VKD3DSPR_SAMPLEMASK:
+        case VKD3DSPR_SAMPLER:
         case VKD3DSPR_SSA:
         case VKD3DSPR_TESSCOORD:
         case VKD3DSPR_THREADGROUPID:
@@ -15529,6 +16094,31 @@ static bool can_propagate_ssa_source(const struct vsir_program *program, const s
     return true;
 }
 
+static bool vsir_program_copy_propagate_relative_addresses(struct vsir_copy_propagation_state *state,
+        struct vsir_operand *reg)
+{
+    bool progress = false;
+
+    for (unsigned int i = 0; i < reg->idx_count; ++i)
+    {
+        const struct vsir_src_operand *rel_addr = reg->idx[i].rel_addr;
+        const struct vkd3d_shader_instruction *mov;
+
+        if (!rel_addr || rel_addr->reg.type != VKD3DSPR_SSA)
+            continue;
+
+        if ((mov = state->ssa_sources[rel_addr->reg.idx[0].offset])
+                && vsir_rel_addr_reg_type_is_valid(mov->src[0].reg.type))
+        {
+            *reg->idx[i].rel_addr = mov->src[0];
+            TRACE("Propagating write to sr%u into relative address.\n", mov->src[0].reg.idx[0].offset);
+            progress = true;
+        }
+    }
+
+    return progress;
+}
+
 static enum vkd3d_result vsir_program_copy_propagation(struct vsir_program *program,
         struct vsir_transformation_context *ctx)
 {
@@ -15549,6 +16139,8 @@ static enum vkd3d_result vsir_program_copy_propagation(struct vsir_program *prog
             enum vsir_data_type data_type;
             uint32_t new_swizzle = 0;
 
+            ctx->progress |= vsir_program_copy_propagate_relative_addresses(&state, &src->reg);
+
             if (src->reg.type != VKD3DSPR_SSA)
                 continue;
             if (data_type_is_64_bit(src->reg.data_type))
@@ -15558,10 +16150,13 @@ static enum vkd3d_result vsir_program_copy_propagation(struct vsir_program *prog
             mov_src = &mov->src[0];
             data_type = src->reg.data_type;
 
+            TRACE("Propagating write to sr%u into %s instruction.\n",
+                    src->reg.idx[0].offset, vsir_opcode_get_name(ins->opcode, "<unknown>"));
+
             src->reg = mov_src->reg;
             src->reg.data_type = data_type;
 
-            if (!shader_register_clone_relative_addresses(&src->reg, program))
+            if (!vsir_operand_clone_indirect_indices(&src->reg, program))
             {
                 vkd3d_free(state.ssa_sources);
                 return VKD3D_ERROR_OUT_OF_MEMORY;
@@ -15596,6 +16191,9 @@ static enum vkd3d_result vsir_program_copy_propagation(struct vsir_program *prog
             ctx->progress = true;
         }
 
+        for (unsigned int j = 0; j < ins->dst_count; ++j)
+            ctx->progress |= vsir_program_copy_propagate_relative_addresses(&state, &ins->dst[j].reg);
+
         if (can_propagate_ssa_source(program, ins))
             state.ssa_sources[ins->dst[0].reg.idx[0].offset] = ins;
     }
@@ -15604,46 +16202,711 @@ static enum vkd3d_result vsir_program_copy_propagation(struct vsir_program *prog
     return VKD3D_OK;
 }
 
-static void vsir_optimize(struct vsir_transformation_context *ctx)
+/*
+ * This pass is similar to vsir_program_copy_propagation(), but operating in
+ * "reverse", hoisting output MOVs earlier by combining them with the
+ * instruction where they're written.
+ *
+ * We attempt to combine two instructions, not necessarily consecutive,
+ * of the form
+ *
+ *      XXX aaa.bbb, ...
+ *      mov ccc.ddd, aaa
+ *
+ * into
+ *
+ *      XXX ccc.ddd, ...
+ *
+ * Note that ddd does not need to be a subset of bbb;
+ * this pass should support temp registers written in pieces.
+ * Currently only SSA is supported, so for now ddd will be a subset of bbb.
+ *
+ * The constraints we operate under are a bit different, and stricter:
+ *
+ *  - aaa.bbb cannot be written between the two instructions.
+ *
+ *  - aaa.bbb must only be read once. This is not a correctness constraint,
+ *    but rather we prefer emitting a mov to duplicating any instruction that
+ *    does real work. We will get rid of the write to aaa.bbb entirely.
+ *
+ *  - Only write-only output registers are supported. While this pass may be
+ *    legal on temps, it's not clear that it will provide any utility there;
+ *    temp copies should already be deleted by regular HLSL or vsir copy prop.
+ *
+ *  - The two instructions must exist in the same control flow. We must write
+ *    an output in exactly the same conditions.
+ */
+struct vsir_output_copy_hoisting_state
+{
+    struct vsir_output_copy_hoisting_reg
+    {
+        struct vkd3d_shader_instruction *source;
+        unsigned int source_dst_index; /* Which dst of 'source' writes this variable. */
+        struct vkd3d_shader_instruction *mov;
+        /* Two instructions A and B are in the same control flow if:
+         *  (1) they have the same depth;
+         *  (2) the CF depth never decreases below A's depth between A and B.
+         * The depth of A (source) is tracked in 'source_depth'.
+         * Condition (2) should be impossible for SSAs. */
+        uint32_t source_depth;
+        bool invalid;
+        bool read_once;
+    } *ssas;
+
+    unsigned int depth;
+};
+
+static bool is_write_only(enum vkd3d_shader_register_type type)
 {
-    do
+    switch (type)
     {
-        ctx->progress = false;
-        vsir_transform(ctx, vsir_program_copy_propagation);
-        vsir_transform(ctx, vsir_program_dce);
+        case VKD3DSPR_ADDR:
+        case VKD3DSPR_IDXTEMP:
+        case VKD3DSPR_LOOP:
+        case VKD3DSPR_TEMP:
+        case VKD3DSPR_TEMPFLOAT16:
+        case VKD3DSPR_TEXTURE:
+            return false;
+
+        /* Not applicable since they're not numeric or can't be destinations. */
+        case VKD3DSPR_COMBINED_SAMPLER:
+        case VKD3DSPR_CONST:
+        case VKD3DSPR_CONSTBOOL:
+        case VKD3DSPR_CONSTBUFFER:
+        case VKD3DSPR_CONSTINT:
+        case VKD3DSPR_COUNT:
+        case VKD3DSPR_COVERAGE:
+        case VKD3DSPR_FORKINSTID:
+        case VKD3DSPR_FUNCTIONBODY:
+        case VKD3DSPR_FUNCTIONPOINTER:
+        case VKD3DSPR_GROUPSHAREDMEM:
+        case VKD3DSPR_GSINSTID:
+        case VKD3DSPR_IMMCONST:
+        case VKD3DSPR_IMMCONST64:
+        case VKD3DSPR_IMMCONSTBUFFER:
+        case VKD3DSPR_INCONTROLPOINT:
+        case VKD3DSPR_INPUT:
+        case VKD3DSPR_INVALID:
+        case VKD3DSPR_JOININSTID:
+        case VKD3DSPR_LABEL:
+        case VKD3DSPR_LOCALTHREADID:
+        case VKD3DSPR_LOCALTHREADINDEX:
+        case VKD3DSPR_MISCTYPE:
+        case VKD3DSPR_NULL:
+        case VKD3DSPR_OUTCONTROLPOINT:
+        case VKD3DSPR_OUTPOINTID:
+        case VKD3DSPR_PARAMETER:
+        case VKD3DSPR_PATCHCONST:
+        case VKD3DSPR_POINT_COORD:
+        case VKD3DSPR_PREDICATE:
+        case VKD3DSPR_PRIMID:
+        case VKD3DSPR_RASTERIZER:
+        case VKD3DSPR_RESOURCE:
+        case VKD3DSPR_SAMPLEMASK:
+        case VKD3DSPR_SAMPLER:
+        case VKD3DSPR_SSA:
+        case VKD3DSPR_STREAM:
+        case VKD3DSPR_TESSCOORD:
+        case VKD3DSPR_THREADGROUPID:
+        case VKD3DSPR_THREADID:
+        case VKD3DSPR_UAV:
+        case VKD3DSPR_UNDEF:
+        case VKD3DSPR_WAVELANECOUNT:
+        case VKD3DSPR_WAVELANEINDEX:
+            return false;
+
+        case VKD3DSPR_ATTROUT:
+        case VKD3DSPR_COLOROUT:
+        case VKD3DSPR_DEPTHOUT:
+        case VKD3DSPR_DEPTHOUTGE:
+        case VKD3DSPR_DEPTHOUTLE:
+        case VKD3DSPR_OUTPUT:
+        case VKD3DSPR_OUTSTENCILREF:
+        case VKD3DSPR_RASTOUT:
+        case VKD3DSPR_TEXCRDOUT:
+            return true;
     }
-    while (ctx->progress);
+
+    vkd3d_unreachable();
 }
 
-enum vkd3d_result vsir_program_optimize(struct vsir_program *program, uint64_t config_flags,
-        const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_message_context *message_context)
+static bool is_identity_swizzle(uint32_t swizzle, uint32_t write_mask)
 {
-    struct vsir_transformation_context ctx;
+    for (unsigned int i = 0; i < VKD3D_VEC4_SIZE; ++i)
+    {
+        if (bitmap_is_set(&write_mask, i) && vsir_swizzle_get_component(swizzle, i) != i)
+            return false;
+    }
 
-    vsir_transformation_context_init(&ctx, program, config_flags, compile_info, message_context);
-    vsir_optimize(&ctx);
+    return true;
+}
 
-    if (TRACE_ON() && ctx.result >= 0)
-        vsir_program_trace(program);
+static void vsir_program_output_copy_hoisting_record_read(
+        const struct vsir_output_copy_hoisting_state *state, const struct vsir_src_operand *src)
+{
+    struct vsir_output_copy_hoisting_reg *reg;
 
-    return ctx.result;
+    if (src->reg.type != VKD3DSPR_SSA)
+        return;
+    reg = &state->ssas[src->reg.idx[0].offset];
+    if (!reg->read_once)
+        reg->read_once = true;
+    else
+        reg->invalid = true;
 }
 
-/* Transformations which should happen at parse time, i.e. before scan
- * information is returned to the user.
- *
- * In particular, some passes need to modify the signature, and
- * vkd3d_shader_scan() should report the modified signature for the given
- * target. */
-enum vkd3d_result vsir_program_transform_early(struct vsir_program *program, uint64_t config_flags,
-        const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_message_context *message_context)
+static void vsir_program_output_copy_hoisting_record_mov(
+        const struct vsir_output_copy_hoisting_state *state, struct vkd3d_shader_instruction *ins)
 {
-    struct vsir_transformation_context ctx;
+    const struct vsir_src_operand *src = &ins->src[0];
+    const struct vsir_dst_operand *dst = &ins->dst[0];
+    struct vsir_output_copy_hoisting_reg *reg;
 
-    vsir_transformation_context_init(&ctx, program, config_flags, compile_info, message_context);
-
-    /* For vsir_program_ensure_diffuse(). */
-    if (program->shader_version.major <= 2)
+    if (src->reg.type != VKD3DSPR_SSA)
+        return;
+    if (!is_write_only(dst->reg.type))
+        return;
+    if (src->modifiers || (dst->modifiers & ~VKD3DSPDM_SATURATE)
+            || dst->shift || data_type_is_64_bit(src->reg.data_type))
+        return;
+    for (unsigned int i = 0; i < dst->reg.idx_count; ++i)
+    {
+        if (dst->reg.idx[i].rel_addr)
+            return;
+    }
+    /* We can sometimes mess with the swizzle in 'source', but for now don't. */
+    if (dst->reg.dimension == VSIR_DIMENSION_VEC4 && !is_identity_swizzle(src->swizzle, dst->write_mask))
+        return;
+    else if (dst->reg.dimension == VSIR_DIMENSION_SCALAR
+            && vsir_swizzle_get_component(src->swizzle, 0) != VKD3D_SHADER_SWIZZLE_X)
+        return;
+
+    reg = &state->ssas[src->reg.idx[0].offset];
+    if (reg->invalid)
+        return;
+    if (reg->mov || reg->source_depth != state->depth)
+    {
+        reg->invalid = true;
+        return;
+    }
+    reg->mov = ins;
+}
+
+static enum vkd3d_result vsir_program_output_copy_hoisting(struct vsir_program *program,
+        struct vsir_transformation_context *ctx)
+{
+    struct vsir_program_iterator it = vsir_program_iterator(&program->instructions);
+    struct vsir_output_copy_hoisting_state state = {0};
+    struct vkd3d_shader_instruction *ins;
+
+    if (!(state.ssas = vkd3d_calloc(program->ssa_count, sizeof(*state.ssas))))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+
+    for (ins = vsir_program_iterator_head(&it); ins; ins = vsir_program_iterator_next(&it))
+    {
+        for (unsigned int j = 0; j < ins->src_count; ++j)
+        {
+            vsir_program_output_copy_hoisting_record_read(&state, &ins->src[j]);
+
+            for (unsigned int k = 0; k < ins->src[j].reg.idx_count; ++k)
+            {
+                if (ins->src[j].reg.idx[k].rel_addr)
+                    vsir_program_output_copy_hoisting_record_read(&state, ins->src[j].reg.idx[k].rel_addr);
+            }
+        }
+
+        for (unsigned int j = 0; j < ins->dst_count; ++j)
+        {
+            for (unsigned int k = 0; k < ins->dst[j].reg.idx_count; ++k)
+            {
+                if (ins->dst[j].reg.idx[k].rel_addr)
+                    vsir_program_output_copy_hoisting_record_read(&state, ins->dst[j].reg.idx[k].rel_addr);
+            }
+        }
+
+        switch (ins->opcode)
+        {
+            case VSIR_OP_IF:
+            case VSIR_OP_IFC:
+            case VSIR_OP_LOOP:
+            case VSIR_OP_SWITCH:
+                ++state.depth;
+                break;
+
+            case VSIR_OP_ENDIF:
+            case VSIR_OP_ENDLOOP:
+            case VSIR_OP_ENDSWITCH:
+                --state.depth;
+                break;
+
+            case VSIR_OP_MOV:
+                vsir_program_output_copy_hoisting_record_mov(&state, ins);
+                break;
+
+            default:
+                break;
+        }
+
+        for (unsigned int j = 0; j < ins->dst_count; ++j)
+        {
+            const struct vsir_dst_operand *dst = &ins->dst[j];
+            struct vsir_output_copy_hoisting_reg *reg;
+
+            if (dst->reg.type != VKD3DSPR_SSA)
+                continue;
+            reg = &state.ssas[dst->reg.idx[0].offset];
+
+            /* This is an SSA; it should not have been written already. */
+            VKD3D_ASSERT(!reg->source);
+            reg->source = ins;
+            reg->source_dst_index = j;
+            reg->source_depth = state.depth;
+        }
+    }
+
+    for (unsigned int i = 0; i < program->ssa_count; ++i)
+    {
+        struct vsir_output_copy_hoisting_reg *reg = &state.ssas[i];
+        struct vsir_dst_operand *source_dst;
+        enum vsir_data_type orig_data_type;
+
+        if (reg->invalid || !reg->mov)
+            continue;
+
+        TRACE("Hoisting 'mov %s%u, sr%u' to %s instruction.\n",
+                vsir_register_type_get_name(reg->mov->dst[0].reg.type, "<unknown>"),
+                reg->mov->dst[0].reg.idx[0].offset,
+                reg->mov->src[0].reg.idx[0].offset,
+                vsir_opcode_get_name(reg->source->opcode, "<unknown>"));
+
+        source_dst = &reg->source->dst[reg->source_dst_index];
+        orig_data_type = source_dst->reg.data_type;
+        source_dst->reg = reg->mov->dst[0].reg;
+        source_dst->reg.data_type = orig_data_type;
+        source_dst->write_mask = reg->mov->dst[0].write_mask;
+        /* We validate that the modifiers must only be SATURATE, which is idempotent. */
+        source_dst->modifiers |= reg->mov->dst[0].modifiers;
+        vkd3d_shader_instruction_make_nop(reg->mov);
+        ctx->progress = true;
+    }
+
+    vkd3d_free(state.ssas);
+    return VKD3D_OK;
+}
+
+static bool vsir_operand_has_rel_addr(const struct vsir_operand *reg)
+{
+    for (unsigned int i = 0; i < reg->idx_count; ++i)
+    {
+        if (reg->idx[i].rel_addr)
+            return true;
+    }
+    return false;
+}
+
+static bool vsir_instruction_is_pure(const struct vsir_program *program, const struct vkd3d_shader_instruction *ins)
+{
+    if (vsir_instruction_has_side_effects(ins))
+        return false;
+
+    for (unsigned int i = 0; i < ins->src_count; ++i)
+    {
+        const struct vsir_operand *reg = &ins->src[i].reg;
+
+        if (!is_read_only(program, reg->type))
+            return false;
+        if (vsir_operand_has_rel_addr(reg))
+            return false;
+
+    }
+
+    for (unsigned int i = 0; i < ins->dst_count; ++i)
+    {
+        if (vsir_operand_has_rel_addr(&ins->dst[i].reg))
+            return false;
+    }
+
+    return true;
+}
+
+static bool vsir_op_is_commutative(enum vkd3d_shader_opcode opcode)
+{
+    switch (opcode)
+    {
+        case VSIR_OP_AND:
+        case VSIR_OP_EQO:
+        case VSIR_OP_EQU:
+        case VSIR_OP_IADD:
+        case VSIR_OP_IEQ:
+        case VSIR_OP_IMAX:
+        case VSIR_OP_IMIN:
+        case VSIR_OP_IMUL:
+        case VSIR_OP_INE:
+        case VSIR_OP_MAX:
+        case VSIR_OP_MIN:
+        case VSIR_OP_NEO:
+        case VSIR_OP_NEU:
+        case VSIR_OP_OR:
+        case VSIR_OP_UMAX:
+        case VSIR_OP_UMIN:
+        case VSIR_OP_XOR:
+            return true;
+
+        default:
+            return false;
+    }
+}
+
+struct vsir_cse_expr
+{
+    struct rb_entry entry;
+
+    struct vkd3d_shader_instruction ins;
+    unsigned int ins_index;
+};
+
+static int vsir_register_index_compare(const struct vsir_register_index *a, const struct vsir_register_index *b)
+{
+    int ret;
+
+    if ((ret = vkd3d_ptr_compare(a->rel_addr, b->rel_addr)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->offset, b->offset)))
+        return ret;
+    return vkd3d_u32_compare(!!a->is_in_bounds, !!b->is_in_bounds);
+}
+
+static int vsir_operand_compare(const struct vsir_operand *a, const struct vsir_operand *b)
+{
+    int ret;
+
+    if ((ret = vkd3d_u32_compare(a->type, b->type)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->precision, b->precision)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(!!a->non_uniform, !!b->non_uniform)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->data_type, b->data_type)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->idx_count, b->idx_count)))
+        return ret;
+    for (unsigned int i = 0; i < a->idx_count; ++i)
+    {
+        if ((ret = vsir_register_index_compare(&a->idx[i], &b->idx[i])))
+            return ret;
+    }
+    if ((ret = vkd3d_u32_compare(a->dimension, b->dimension)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->alignment, b->alignment)))
+        return ret;
+
+    if (a->type == VKD3DSPR_IMMCONST)
+    {
+        unsigned int count;
+
+        VKD3D_ASSERT(a->dimension == VSIR_DIMENSION_SCALAR || a->dimension == VSIR_DIMENSION_VEC4);
+        count = a->dimension == VSIR_DIMENSION_VEC4 ? 4 : 1;
+
+        return memcmp(a->u.immconst_u32, b->u.immconst_u32, sizeof(a->u.immconst_u32[0]) * count);
+    }
+    else if (a->type == VKD3DSPR_IMMCONST64)
+    {
+        unsigned int count;
+
+        VKD3D_ASSERT(a->dimension == VSIR_DIMENSION_SCALAR || a->dimension == VSIR_DIMENSION_VEC4);
+        count = a->dimension == VSIR_DIMENSION_VEC4 ? 2 : 1;
+
+        return memcmp(a->u.immconst_u64, b->u.immconst_u64, sizeof(a->u.immconst_u64[0]) * count);
+    }
+
+    return 0;
+}
+
+static int vsir_src_operand_compare(const struct vsir_src_operand *a, const struct vsir_src_operand *b)
+{
+    int ret;
+
+    if ((ret = vkd3d_u32_compare(a->swizzle, b->swizzle)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->modifiers, b->modifiers)))
+        return ret;
+    return vsir_operand_compare(&a->reg, &b->reg);
+}
+
+static int vsir_cse_expr_key_compare(const void *key, const struct rb_entry *e)
+{
+    const struct vkd3d_shader_instruction *a = &((struct vsir_cse_expr *)key)->ins;
+    const struct vkd3d_shader_instruction *b = &RB_ENTRY_VALUE(e, struct vsir_cse_expr, entry)->ins;
+    int ret;
+
+    if ((ret = vkd3d_u32_compare(a->opcode, b->opcode)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->flags, b->flags)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->dst->write_mask, b->dst->write_mask)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->dst->modifiers, b->dst->modifiers)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->dst->shift, b->dst->shift)))
+        return ret;
+
+    VKD3D_ASSERT(a->src_count == b->src_count);
+    for (unsigned int i = 0; i < a->src_count; ++i)
+    {
+        if ((ret = vsir_src_operand_compare(&a->src[i], &b->src[i])))
+            return ret;
+    }
+
+    if ((ret = vkd3d_u32_compare(a->texel_offset.u, b->texel_offset.u)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->texel_offset.v, b->texel_offset.v)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->texel_offset.w, b->texel_offset.w)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->resource_type, b->resource_type)))
+        return ret;
+    if ((ret = vkd3d_u32_compare(a->resource_stride, b->resource_stride)))
+        return ret;
+    return memcmp(a->resource_data_type, b->resource_data_type, sizeof(a->resource_data_type));
+}
+
+static void vsir_cse_expr_destroy(struct vsir_cse_expr *expr)
+{
+    vkd3d_free(expr->ins.src);
+    vkd3d_free(expr);
+}
+
+static struct vsir_cse_expr *vsir_cse_make_expr(const struct vkd3d_shader_instruction *ins, unsigned int ins_index)
+{
+    struct vsir_cse_expr *expr;
+
+    if (!(expr = vkd3d_calloc(sizeof(*expr), 1)))
+        return NULL;
+
+    expr->ins.opcode = ins->opcode;
+    expr->ins.flags = ins->flags;
+
+    VKD3D_ASSERT(ins->dst_count == 1);
+    expr->ins.dst_count = 1;
+    expr->ins.dst = ins->dst;
+
+    expr->ins.src_count = ins->src_count;
+
+    if (!(expr->ins.src = vkd3d_malloc(sizeof(*expr->ins.src) * ins->src_count)))
+    {
+        vkd3d_free(expr);
+        return NULL;
+    }
+
+    if (vsir_op_is_commutative(ins->opcode))
+    {
+        /* Normalize operand order for commutative operations. */
+        if (vsir_src_operand_compare(&ins->src[0], &ins->src[1]) <= 0)
+        {
+            expr->ins.src[0] = ins->src[0];
+            expr->ins.src[1] = ins->src[1];
+        }
+        else
+        {
+            expr->ins.src[0] = ins->src[1];
+            expr->ins.src[1] = ins->src[0];
+        }
+    }
+    else
+    {
+        memcpy(expr->ins.src, ins->src, expr->ins.src_count * sizeof(expr->ins.src[0]));
+    }
+
+    expr->ins.texel_offset = ins->texel_offset;
+
+    expr->ins.resource_type = ins->resource_type;
+    expr->ins.resource_stride = ins->resource_stride;
+    memcpy(expr->ins.resource_data_type, ins->resource_data_type, sizeof(expr->ins.resource_data_type));
+
+    expr->ins_index = ins_index;
+
+    return expr;
+}
+
+struct vsir_cse_state
+{
+    struct rb_tree *expr_tables;
+    size_t capacity;
+    unsigned int depth;
+};
+
+static void vsir_cse_free_expr(struct rb_entry *e, void *context)
+{
+    struct vsir_cse_expr *expr = RB_ENTRY_VALUE(e, struct vsir_cse_expr, entry);
+
+    vsir_cse_expr_destroy(expr);
+}
+
+static struct vsir_cse_expr *vsir_cse_find_expr(struct vsir_cse_state *state, const struct vsir_cse_expr *key)
+{
+    for (int i = state->depth; i >= 0; --i)
+    {
+        struct rb_entry *entry = rb_get(&state->expr_tables[i], key);
+
+        if (entry)
+            return RB_ENTRY_VALUE(entry, struct vsir_cse_expr, entry);
+    }
+    return NULL;
+}
+
+static enum vkd3d_result vsir_program_cse(struct vsir_program *program,
+        struct vsir_transformation_context *ctx)
+{
+    struct vsir_program_iterator it = vsir_program_iterator(&program->instructions);
+    struct vkd3d_shader_instruction *ins;
+    struct vsir_cse_state state = {0};
+    unsigned int ins_idx;
+
+    if (!vkd3d_array_reserve((void **)&state.expr_tables, &state.capacity, 1, sizeof(*state.expr_tables)))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+    rb_init(state.expr_tables, vsir_cse_expr_key_compare);
+
+    for (ins = vsir_program_iterator_head(&it), ins_idx = 0; ins; ins = vsir_program_iterator_next(&it), ++ins_idx)
+    {
+        struct vsir_cse_expr *expr, *prev_expr;
+
+        switch (ins->opcode)
+        {
+            case VSIR_OP_IF:
+            case VSIR_OP_IFC:
+            case VSIR_OP_LOOP:
+            case VSIR_OP_SWITCH:
+                ++state.depth;
+                if (!vkd3d_array_reserve((void **)&state.expr_tables, &state.capacity,
+                        state.depth + 1, sizeof(*state.expr_tables)))
+                    return VKD3D_ERROR_OUT_OF_MEMORY;
+                rb_init(&state.expr_tables[state.depth], vsir_cse_expr_key_compare);
+                continue;
+
+            case VSIR_OP_ENDIF:
+            case VSIR_OP_ENDLOOP:
+            case VSIR_OP_ENDSWITCH:
+                rb_destroy(&state.expr_tables[state.depth], vsir_cse_free_expr, NULL);
+                --state.depth;
+                continue;
+
+            case VSIR_OP_ELSE:
+            case VSIR_OP_CASE:
+            case VSIR_OP_DEFAULT:
+            case VSIR_OP_HS_CONTROL_POINT_PHASE:
+            case VSIR_OP_HS_FORK_PHASE:
+            case VSIR_OP_HS_JOIN_PHASE:
+                rb_destroy(&state.expr_tables[state.depth], vsir_cse_free_expr, NULL);
+                rb_init(&state.expr_tables[state.depth], vsir_cse_expr_key_compare);
+                continue;
+
+            case VSIR_OP_MOV:
+                continue;
+
+            default:
+                break;
+        }
+
+        if (ins->dst_count != 1)
+            continue;
+        if (!ins->src_count)
+            continue;
+        if (!vsir_instruction_is_pure(program, ins))
+            continue;
+
+        if (!(expr = vsir_cse_make_expr(ins, ins_idx)))
+            return VKD3D_ERROR_OUT_OF_MEMORY;
+
+        if ((prev_expr = vsir_cse_find_expr(&state, expr)))
+        {
+            struct vkd3d_shader_instruction *mov;
+
+            vsir_cse_expr_destroy(expr);
+
+            /* Replace the current instruction with a MOV from the existing SSA. */
+            if (!vsir_program_iterator_insert_after(&it, 1))
+                return VKD3D_ERROR_OUT_OF_MEMORY;
+            ins = vsir_program_iterator_current(&it);
+            mov = vsir_program_iterator_next(&it);
+
+            if (!(vsir_instruction_init_with_params(program, mov, &ins->location, VSIR_OP_MOV, 1, 1)))
+                return VKD3D_ERROR_OUT_OF_MEMORY;
+
+            mov->dst[0] = ins->dst[0];
+
+            VKD3D_ASSERT(prev_expr->ins.dst->reg.type == VKD3DSPR_SSA);
+            vsir_src_operand_init_ssa(&mov->src[0], prev_expr->ins.dst->reg.idx[0].offset,
+                prev_expr->ins.dst->reg.data_type, prev_expr->ins.dst->reg.dimension);
+            mov->src[0].swizzle = vsir_swizzle_from_writemask(prev_expr->ins.dst->write_mask);
+
+            vkd3d_shader_instruction_make_nop(ins);
+            ctx->progress = true;
+
+            TRACE("Replaced @%u with a mov from @%u.\n", ins_idx, prev_expr->ins_index);
+        }
+        else if (ins->dst[0].reg.type == VKD3DSPR_SSA)
+        {
+            if (rb_put(&state.expr_tables[state.depth], expr, &expr->entry) == -1)
+            {
+                vsir_cse_expr_destroy(expr);
+                return VKD3D_ERROR_OUT_OF_MEMORY;
+            }
+        }
+        else
+        {
+            vsir_cse_expr_destroy(expr);
+        }
+    }
+
+    VKD3D_ASSERT(state.depth == 0);
+    rb_destroy(&state.expr_tables[0], vsir_cse_free_expr, NULL);
+    vkd3d_free(state.expr_tables);
+
+    return VKD3D_OK;
+}
+
+static void vsir_optimize(struct vsir_transformation_context *ctx)
+{
+    do
+    {
+        ctx->progress = false;
+        vsir_transform(ctx, vsir_program_copy_propagation);
+        vsir_transform(ctx, vsir_program_output_copy_hoisting);
+        vsir_transform(ctx, vsir_program_cse);
+        vsir_transform(ctx, vsir_program_dce);
+    }
+    while (ctx->progress);
+}
+
+enum vkd3d_result vsir_program_optimize(struct vsir_program *program, uint64_t config_flags,
+        const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_message_context *message_context)
+{
+    struct vsir_transformation_context ctx;
+
+    vsir_transformation_context_init(&ctx, program, config_flags, compile_info, message_context);
+    vsir_optimize(&ctx);
+
+    if (TRACE_ON() && ctx.result >= 0)
+        vsir_program_trace(program);
+
+    return ctx.result;
+}
+
+/* Transformations which should happen at parse time, i.e. before scan
+ * information is returned to the user.
+ *
+ * In particular, some passes need to modify the signature, and
+ * vkd3d_shader_scan() should report the modified signature for the given
+ * target. */
+enum vkd3d_result vsir_program_transform_early(struct vsir_program *program, uint64_t config_flags,
+        const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_message_context *message_context)
+{
+    struct vsir_transformation_context ctx;
+
+    vsir_transformation_context_init(&ctx, program, config_flags, compile_info, message_context);
+
+    /* For vsir_program_ensure_diffuse(). */
+    if (program->shader_version.major <= 2)
         vsir_transform(&ctx, vsir_program_add_diffuse_output);
 
     /* For vsir_program_insert_fragment_fog(). */
@@ -15672,6 +16935,11 @@ enum vkd3d_result vsir_program_lower_d3dbc(struct vsir_program *program, uint64_
         vsir_transform(&ctx, vsir_program_normalise_ps1_output);
     }
 
+    if (program->shader_version.major == 3 && program->shader_version.type == VKD3D_SHADER_TYPE_VERTEX)
+        vsir_transform(&ctx, vsir_program_normalise_vs3_point_size_output);
+    if (program->shader_version.major <= 2 && program->shader_version.type == VKD3D_SHADER_TYPE_VERTEX)
+        vsir_transform(&ctx, vsir_program_clamp_vs2_color_output);
+
     if (TRACE_ON() && ctx.result >= 0)
         vsir_program_trace(program);
 
diff --git a/libs/vkd3d/libs/vkd3d-shader/msl.c b/libs/vkd3d/libs/vkd3d-shader/msl.c
index 2049871752c7..00604c240311 100644
--- a/libs/vkd3d/libs/vkd3d-shader/msl.c
+++ b/libs/vkd3d/libs/vkd3d-shader/msl.c
@@ -73,13 +73,15 @@ struct msl_resource_type_info
 static void msl_print_subscript(struct vkd3d_string_buffer *buffer, struct msl_generator *gen,
         const struct vsir_src_operand *rel_addr, unsigned int offset);
 
-static void VKD3D_PRINTF_FUNC(3, 4) msl_compiler_error(struct msl_generator *gen,
-        enum vkd3d_shader_error error, const char *fmt, ...)
+#define msl_compiler_error(gen, error, ...) \
+        msl_compiler_error_(gen, error, __FUNCTION__, __VA_ARGS__)
+static void VKD3D_PRINTF_FUNC(4, 5) msl_compiler_error_(struct msl_generator *gen,
+        enum vkd3d_shader_error error, const char *function, const char *fmt, ...)
 {
     va_list args;
 
     va_start(args, fmt);
-    vkd3d_shader_verror(gen->message_context, &gen->location, error, fmt, args);
+    vkd3d_shader_verror(gen->message_context, &gen->location, error, function, fmt, args);
     va_end(args);
     gen->failed = true;
 }
@@ -307,7 +309,7 @@ static void msl_print_uav_name(struct vkd3d_string_buffer *buffer, struct msl_ge
 }
 
 static enum msl_data_type msl_print_register_name(struct vkd3d_string_buffer *buffer,
-        struct msl_generator *gen, const struct vkd3d_shader_register *reg)
+        struct msl_generator *gen, const struct vsir_operand *reg)
 {
     const struct vkd3d_shader_descriptor_info1 *descriptor;
     unsigned int binding, cbv_id, cbv_idx;
@@ -458,6 +460,14 @@ static enum msl_data_type msl_print_register_name(struct vkd3d_string_buffer *bu
             vkd3d_string_buffer_printf(buffer, "v_local_thread_index");
             return MSL_DATA_UNION;
 
+        case VKD3DSPR_OUTSTENCILREF:
+            if (gen->program->shader_version.type != VKD3D_SHADER_TYPE_PIXEL)
+                msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
+                        "Internal compiler error: Unhandled stencil reference output in shader type #%x.",
+                        gen->program->shader_version.type);
+            vkd3d_string_buffer_printf(buffer, "o_stencil_ref");
+            return MSL_DATA_UNION;
+
         case VKD3DSPR_UNDEF:
             switch (reg->dimension)
             {
@@ -556,7 +566,7 @@ static void msl_print_bitcast(struct vkd3d_string_buffer *dst, struct msl_genera
 static void msl_print_src_with_type(struct vkd3d_string_buffer *buffer, struct msl_generator *gen,
     const struct vsir_src_operand *vsir_src, uint32_t mask, enum vsir_data_type data_type)
 {
-    const struct vkd3d_shader_register *reg = &vsir_src->reg;
+    const struct vsir_operand *reg = &vsir_src->reg;
     struct vkd3d_string_buffer *register_name;
     enum msl_data_type src_data_type;
 
@@ -1957,6 +1967,12 @@ static void msl_generate_output_struct_declarations(struct msl_generator *gen)
         vkd3d_string_buffer_printf(buffer, "uint shader_out_mask [[sample_mask]];\n");
     }
 
+    if (bitmap_is_set(gen->program->io_dcls, VKD3DSPR_OUTSTENCILREF))
+    {
+        msl_print_indent(gen->buffer, 1);
+        vkd3d_string_buffer_printf(buffer, "uint shader_out_stencil_ref [[stencil]];\n");
+    }
+
     vkd3d_string_buffer_printf(buffer, "};\n\n");
 }
 
@@ -2132,6 +2148,8 @@ static void msl_generate_entrypoint_epilogue(struct msl_generator *gen)
 
     if (bitmap_is_set(gen->program->io_dcls, VKD3DSPR_SAMPLEMASK))
         vkd3d_string_buffer_printf(gen->buffer, "    output.shader_out_mask = o_mask.u;\n");
+    if (bitmap_is_set(gen->program->io_dcls, VKD3DSPR_OUTSTENCILREF))
+        vkd3d_string_buffer_printf(gen->buffer, "    output.shader_out_stencil_ref = o_stencil_ref.u;\n");
 }
 
 static void msl_generate_entrypoint(struct msl_generator *gen)
@@ -2220,6 +2238,8 @@ static void msl_generate_entrypoint(struct msl_generator *gen)
         vkd3d_string_buffer_printf(gen->buffer, "    vkd3d_vec4 v_local_thread_id;\n");
     if (bitmap_is_set(gen->program->io_dcls, VKD3DSPR_LOCALTHREADINDEX))
         vkd3d_string_buffer_printf(gen->buffer, "    vkd3d_vec4 v_local_thread_index;\n");
+    if (bitmap_is_set(gen->program->io_dcls, VKD3DSPR_OUTSTENCILREF))
+        vkd3d_string_buffer_printf(gen->buffer, "    vkd3d_scalar o_stencil_ref;\n");
     vkd3d_string_buffer_printf(gen->buffer, "\n");
 
     msl_generate_entrypoint_prologue(gen);
@@ -2239,6 +2259,8 @@ static void msl_generate_entrypoint(struct msl_generator *gen)
         vkd3d_string_buffer_printf(gen->buffer, ", v_local_thread_id");
     if (bitmap_is_set(gen->program->io_dcls, VKD3DSPR_LOCALTHREADINDEX))
         vkd3d_string_buffer_printf(gen->buffer, ", v_local_thread_index");
+    if (bitmap_is_set(gen->program->io_dcls, VKD3DSPR_OUTSTENCILREF))
+        vkd3d_string_buffer_printf(gen->buffer, ", o_stencil_ref");
     if (gen->program->descriptors.descriptor_count)
         vkd3d_string_buffer_printf(gen->buffer, ", descriptors");
     vkd3d_string_buffer_printf(gen->buffer, ");\n\n");
@@ -2252,18 +2274,39 @@ static void msl_generate_entrypoint(struct msl_generator *gen)
 
 static int msl_generator_generate(struct msl_generator *gen, struct vkd3d_shader_code *out)
 {
+    enum vsir_global_flags flags = gen->program->global_flags;
     struct vkd3d_shader_instruction *ins;
     struct vsir_program_iterator it;
 
+    static const uint64_t ignored_flags = VKD3DSGF_REFACTORING_ALLOWED
+            | VKD3DSGF_FORCE_EARLY_DEPTH_STENCIL
+            | VKD3DSGF_BIND_FOR_DURATION
+            | VKD3DSGF_ENABLE_STENCIL_REF;
+
     MESSAGE("Generating a MSL shader. This is unsupported; you get to keep all the pieces if it breaks.\n");
 
     vkd3d_string_buffer_printf(gen->buffer, "/* Generated by %s. */\n\n", vkd3d_shader_get_version(NULL, NULL));
     vkd3d_string_buffer_printf(gen->buffer, "#include <metal_stdlib>\n");
     vkd3d_string_buffer_printf(gen->buffer, "using namespace metal;\n\n");
 
-    if (gen->program->global_flags & ~(VKD3DSGF_REFACTORING_ALLOWED | VKD3DSGF_FORCE_EARLY_DEPTH_STENCIL))
+    if (flags & ignored_flags)
+    {
+        TRACE("Ignoring global flags %#"PRIx64".\n", flags & ignored_flags);
+        flags &= ~ignored_flags;
+    }
+
+    if (flags)
         msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
-                "Internal compiler error: Unhandled global flags %#"PRIx64".", (uint64_t)gen->program->global_flags);
+                "Internal compiler error: Unhandled global flags %#"PRIx64".", (uint64_t)flags);
+
+    if (gen->program->f16_denormal_mode != VKD3D_SHADER_DENORMAL_MODE_ANY
+            || gen->program->f32_denormal_mode != VKD3D_SHADER_DENORMAL_MODE_ANY
+            || gen->program->f64_denormal_mode != VKD3D_SHADER_DENORMAL_MODE_ANY)
+    {
+        msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_UNSUPPORTED,
+                "Cannot emit denormal modes. The target environment does not support float controls.");
+        return VKD3D_ERROR;
+    }
 
     vkd3d_string_buffer_printf(gen->buffer, "union vkd3d_scalar\n{\n");
     vkd3d_string_buffer_printf(gen->buffer, "    uint u;\n");
@@ -2320,6 +2363,8 @@ static int msl_generator_generate(struct msl_generator *gen, struct vkd3d_shader
         vkd3d_string_buffer_printf(gen->buffer, ", thread vkd3d_vec4 &v_local_thread_id");
     if (bitmap_is_set(gen->program->io_dcls, VKD3DSPR_LOCALTHREADINDEX))
         vkd3d_string_buffer_printf(gen->buffer, ", thread vkd3d_vec4 &v_local_thread_index");
+    if (bitmap_is_set(gen->program->io_dcls, VKD3DSPR_OUTSTENCILREF))
+        vkd3d_string_buffer_printf(gen->buffer, ", thread vkd3d_scalar &o_stencil_ref");
     if (gen->program->descriptors.descriptor_count)
         vkd3d_string_buffer_printf(gen->buffer, ", constant descriptor *descriptors");
     vkd3d_string_buffer_printf(gen->buffer, ")\n{\n");
diff --git a/libs/vkd3d/libs/vkd3d-shader/preproc.h b/libs/vkd3d/libs/vkd3d-shader/preproc.h
index 9217237d8d3a..c1d752ccb7aa 100644
--- a/libs/vkd3d/libs/vkd3d-shader/preproc.h
+++ b/libs/vkd3d/libs/vkd3d-shader/preproc.h
@@ -64,7 +64,7 @@ struct preproc_expansion
     struct preproc_text *arg_values;
     /* Back-pointer to the macro, if this expansion a macro body. This is
      * necessary so that argument tokens can be correctly replaced. */
-    struct preproc_macro *macro;
+    const struct preproc_macro *macro;
 };
 
 struct preproc_macro
@@ -109,7 +109,7 @@ struct preproc_ctx
      */
     struct preproc_func_state
     {
-        struct preproc_macro *macro;
+        const struct preproc_macro *macro;
         size_t arg_count;
         enum
         {
@@ -137,8 +137,10 @@ void preproc_close_include(struct preproc_ctx *ctx, const struct vkd3d_shader_co
 struct preproc_macro *preproc_find_macro(struct preproc_ctx *ctx, const char *name);
 void preproc_free_macro(struct preproc_macro *macro);
 bool preproc_push_include(struct preproc_ctx *ctx, char *filename, const struct vkd3d_shader_code *code);
-void preproc_warning(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc,
-        enum vkd3d_shader_error error, const char *format, ...) VKD3D_PRINTF_FUNC(4, 5);
+#define preproc_warning(ctx, loc, error, ...) \
+        preproc_warning_(ctx, loc, error, __FUNCTION__, __VA_ARGS__)
+void preproc_warning_(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...) VKD3D_PRINTF_FUNC(5, 6);
 
 static inline struct preproc_file *preproc_get_top_file(struct preproc_ctx *ctx)
 {
diff --git a/libs/vkd3d/libs/vkd3d-shader/preproc.l b/libs/vkd3d/libs/vkd3d-shader/preproc.l
index d7130f57c34e..9b7760ebb485 100644
--- a/libs/vkd3d/libs/vkd3d-shader/preproc.l
+++ b/libs/vkd3d/libs/vkd3d-shader/preproc.l
@@ -18,6 +18,10 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+%top{
+#include "config.h"
+}
+
 %{
 
 #include "preproc.tab.h"
@@ -211,19 +215,23 @@ INT_SUFFIX      [uUlL]{0,2}
 
 %%
 
-static void preproc_func_state_cleanup_arg_values(struct preproc_func_state *f)
+static void preproc_func_state_cleanup(struct preproc_func_state *f)
 {
-    const struct preproc_macro *m;
+    const struct preproc_macro *m = f->macro;
     size_t i;
 
-    if (!(m = f->macro))
-        return;
-
-    for (i = 0; i < m->arg_count; ++i)
+    if (f->arg_values)
     {
-        vkd3d_string_buffer_cleanup(&f->arg_values[i].text);
+        for (i = 0; i < m->arg_count; ++i)
+        {
+            vkd3d_string_buffer_cleanup(&f->arg_values[i].text);
+        }
+        free(f->arg_values);
+        f->arg_values = NULL;
     }
-    free(f->arg_values);
+    f->state = STATE_NONE;
+    f->arg_count = 0;
+    f->macro = NULL;
 }
 
 static void update_location(struct preproc_ctx *ctx)
@@ -265,7 +273,7 @@ static bool preproc_is_writing(struct preproc_ctx *ctx)
  * function-like macro bodies and their arguments. */
 static bool should_concat(struct preproc_ctx *ctx)
 {
-    struct preproc_macro *macro;
+    const struct preproc_macro *macro;
 
     if (!ctx->expansion_count)
         return false;
@@ -368,7 +376,7 @@ static void preproc_text_add(struct preproc_text *text, const char *string)
 }
 
 static bool preproc_push_expansion(struct preproc_ctx *ctx,
-        const struct preproc_text *text, struct preproc_macro *macro, struct preproc_text *arg_values)
+        const struct preproc_text *text, const struct preproc_macro *macro, struct preproc_text *arg_values)
 {
     struct preproc_expansion *exp;
 
@@ -674,8 +682,7 @@ int yylex(YYSTYPE *lval, YYLTYPE *lloc, yyscan_t scanner)
                     const char *name = func_state->macro->name;
 
                     ctx->lookahead_token = token;
-                    func_state->macro = NULL;
-                    func_state->state = STATE_NONE;
+                    preproc_func_state_cleanup(func_state);
 
                     if (ctx->current_directive)
                         return return_token(T_IDENTIFIER, lval, name);
@@ -740,31 +747,34 @@ int yylex(YYSTYPE *lval, YYLTYPE *lloc, yyscan_t scanner)
                     case '}':
                         if (!--func_state->paren_depth)
                         {
+                            const struct preproc_macro *macro = func_state->macro;
+
                             /* Not only can you end a macro with ] or }, if
                              * you do, it's treated as part of the last
                              * argument. */
                             if (token != ')' && current_arg)
                                 preproc_text_add(current_arg, text);
 
-                            if (++func_state->arg_count == func_state->macro->arg_count)
+                            if (++func_state->arg_count == macro->arg_count)
                             {
-                                preproc_push_expansion(ctx, &func_state->macro->body,
-                                        func_state->macro, func_state->arg_values);
+                                preproc_push_expansion(ctx, &macro->body, macro, func_state->arg_values);
+                                func_state->arg_values = NULL;
                             }
                             else
                             {
                                 preproc_warning(ctx, lloc, VKD3D_SHADER_WARNING_PP_ARGUMENT_COUNT_MISMATCH,
                                         "Wrong number of arguments to macro \"%s\": expected %zu, got %zu.",
-                                        func_state->macro->name, func_state->macro->arg_count, func_state->arg_count);
+                                        macro->name, macro->arg_count, func_state->arg_count);
 
                                 if (ctx->current_directive)
-                                    return return_token(T_IDENTIFIER, lval, func_state->macro->name);
+                                {
+                                    preproc_func_state_cleanup(func_state);
+                                    return return_token(T_IDENTIFIER, lval, macro->name);
+                                }
 
-                                vkd3d_string_buffer_printf(&ctx->buffer, "%s ", func_state->macro->name);
-                                preproc_func_state_cleanup_arg_values(func_state);
+                                vkd3d_string_buffer_printf(&ctx->buffer, "%s ", macro->name);
                             }
-                            func_state->macro = NULL;
-                            func_state->state = STATE_NONE;
+                            preproc_func_state_cleanup(func_state);
                         }
                         else
                         {
@@ -887,6 +897,18 @@ int preproc_lexer_parse(const struct vkd3d_shader_compile_info *compile_info,
 
     preproc_yyparse(ctx.scanner, &ctx);
 
+    switch (ctx.directive_func.state)
+    {
+        case STATE_NONE:
+        case STATE_IDENTIFIER:
+            break;
+
+        case STATE_ARGS:
+            preproc_warning(&ctx, &loc, VKD3D_SHADER_WARNING_PP_UNTERMINATED_MACRO,
+                    "Unterminated macro invocation.");
+            break;
+    }
+
     switch (ctx.text_func.state)
     {
         case STATE_NONE:
@@ -902,7 +924,8 @@ int preproc_lexer_parse(const struct vkd3d_shader_compile_info *compile_info,
             break;
     }
 
-    preproc_func_state_cleanup_arg_values(&ctx.text_func);
+    preproc_func_state_cleanup(&ctx.directive_func);
+    preproc_func_state_cleanup(&ctx.text_func);
     while (ctx.file_count)
         preproc_pop_buffer(&ctx);
     yylex_destroy(ctx.scanner);
diff --git a/libs/vkd3d/libs/vkd3d-shader/preproc.y b/libs/vkd3d/libs/vkd3d-shader/preproc.y
index 95987831faaa..7432bb12d2e7 100644
--- a/libs/vkd3d/libs/vkd3d-shader/preproc.y
+++ b/libs/vkd3d/libs/vkd3d-shader/preproc.y
@@ -24,7 +24,6 @@
 #include "vkd3d_shader_private.h"
 #include "preproc.h"
 #include <stdio.h>
-#include <sys/stat.h>
 
 #define PREPROC_YYLTYPE struct vkd3d_shader_location
 
@@ -45,31 +44,30 @@ int preproc_yylex(PREPROC_YYSTYPE *yylval_param, PREPROC_YYLTYPE *yylloc_param,
 
 %code
 {
+#include "vkd3d_shader_utils.h"
 
 #define YYLLOC_DEFAULT(cur, rhs, n) (cur) = YYRHSLOC(rhs, !!n)
 
-#ifndef S_ISREG
-# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
-#endif
-
-static void preproc_error(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc,
-        enum vkd3d_shader_error error, const char *format, ...)
+#define preproc_error(ctx, loc, error, ...) \
+        preproc_error_(ctx, loc, error, __FUNCTION__, __VA_ARGS__)
+static void VKD3D_PRINTF_FUNC(5, 6) preproc_error_(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_verror(ctx->message_context, loc, error, format, args);
+    vkd3d_shader_verror(ctx->message_context, loc, error, function, format, args);
     va_end(args);
     ctx->error = true;
 }
 
-void preproc_warning(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc,
-        enum vkd3d_shader_error error, const char *format, ...)
+void preproc_warning_(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_vwarning(ctx->message_context, loc, error, format, args);
+    vkd3d_shader_vwarning(ctx->message_context, loc, error, function, format, args);
     va_end(args);
 }
 
@@ -155,11 +153,7 @@ static bool preproc_push_if(struct preproc_ctx *ctx, bool condition)
 static int default_open_include(const char *filename, bool local,
         const char *parent_data, void *context, struct vkd3d_shader_code *out)
 {
-    uint8_t *data, *new_data;
-    size_t size = 4096;
-    struct stat st;
-    size_t pos = 0;
-    size_t ret;
+    enum vkd3d_result res;
     FILE *f;
 
     if (!(f = fopen(filename, "rb")))
@@ -168,63 +162,9 @@ static int default_open_include(const char *filename, bool local,
         return VKD3D_ERROR;
     }
 
-    if (fstat(fileno(f), &st) == -1)
-    {
-        ERR("Could not stat file %s.\n", debugstr_a(filename));
-        fclose(f);
-        return VKD3D_ERROR;
-    }
-
-    if (S_ISREG(st.st_mode))
-        size = st.st_size;
-
-    if (!size)
-    {
-        fclose(f);
-
-        out->code = NULL;
-        out->size = 0;
-
-        return VKD3D_OK;
-    }
-
-    if (!(data = vkd3d_malloc(size)))
-    {
-        fclose(f);
-        return VKD3D_ERROR_OUT_OF_MEMORY;
-    }
-
-    for (;;)
-    {
-        if (pos >= size)
-        {
-            if (size > SIZE_MAX / 2 || !(new_data = vkd3d_realloc(data, size * 2)))
-            {
-                vkd3d_free(data);
-                fclose(f);
-                return VKD3D_ERROR_OUT_OF_MEMORY;
-            }
-            data = new_data;
-            size *= 2;
-        }
-
-        if (!(ret = fread(&data[pos], 1, size - pos, f)))
-            break;
-        pos += ret;
-    }
-
-    if (!feof(f))
-    {
-        vkd3d_free(data);
-        return VKD3D_ERROR;
-    }
-
+    res = vkd3d_shader_code_from_file(out, f);
     fclose(f);
-
-    out->code = data;
-    out->size = pos;
-
-    return VKD3D_OK;
+    return res;
 }
 
 static void default_close_include(const struct vkd3d_shader_code *code, void *context)
diff --git a/libs/vkd3d/libs/vkd3d-shader/spirv.c b/libs/vkd3d/libs/vkd3d-shader/spirv.c
index dd62f34eb084..be798c147cdd 100644
--- a/libs/vkd3d/libs/vkd3d-shader/spirv.c
+++ b/libs/vkd3d/libs/vkd3d-shader/spirv.c
@@ -38,7 +38,7 @@
 #define VKD3D_SPIRV_VERSION_1_0 0x00010000
 #define VKD3D_SPIRV_VERSION_1_3 0x00010300
 #define VKD3D_SPIRV_GENERATOR_ID 18
-#define VKD3D_SPIRV_GENERATOR_VERSION 19
+#define VKD3D_SPIRV_GENERATOR_VERSION 20
 #define VKD3D_SPIRV_GENERATOR_MAGIC vkd3d_make_u32(VKD3D_SPIRV_GENERATOR_VERSION, VKD3D_SPIRV_GENERATOR_ID)
 #ifndef VKD3D_SHADER_UNSUPPORTED_SPIRV_PARSER
 # define VKD3D_SHADER_UNSUPPORTED_SPIRV_PARSER 0
@@ -236,24 +236,28 @@ struct spirv_parser
     struct vkd3d_string_buffer *text;
 };
 
-static void VKD3D_PRINTF_FUNC(3, 4) spirv_parser_error(struct spirv_parser *parser,
-        enum vkd3d_shader_error error, const char *format, ...)
+#define spirv_parser_error(parser, error, ...) \
+        spirv_parser_error_(parser, error, __FUNCTION__, __VA_ARGS__)
+static void VKD3D_PRINTF_FUNC(4, 5) spirv_parser_error_(struct spirv_parser *parser,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_verror(parser->message_context, &parser->location, error, format, args);
+    vkd3d_shader_verror(parser->message_context, &parser->location, error, function, format, args);
     va_end(args);
     parser->failed = true;
 }
 
-static void VKD3D_PRINTF_FUNC(3, 4) spirv_parser_warning(struct spirv_parser *parser,
-        enum vkd3d_shader_error error, const char *format, ...)
+#define spirv_parser_warning(parser, error, ...) \
+        spirv_parser_warning_(parser, error, __FUNCTION__, __VA_ARGS__)
+static void VKD3D_PRINTF_FUNC(4, 5) spirv_parser_warning_(struct spirv_parser *parser,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_vwarning(parser->message_context, &parser->location, error, format, args);
+    vkd3d_shader_vwarning(parser->message_context, &parser->location, error, function, format, args);
     va_end(args);
 }
 
@@ -840,7 +844,7 @@ static void vkd3d_spirv_dump(const struct vkd3d_shader_code *spirv, enum vkd3d_s
 
     if (!vkd3d_spirv_binary_to_text(spirv, NULL, environment, formatting, &text, &message_context))
     {
-        vkd3d_shader_trace_text(text.code, text.size);
+        TRACE_TEXT(text.code, text.size);
         vkd3d_shader_free_shader_code(&text);
     }
 
@@ -2587,6 +2591,9 @@ static bool vkd3d_spirv_compile_module(struct vkd3d_spirv_builder *builder,
         vkd3d_spirv_build_op_extension(&stream, "SPV_EXT_shader_stencil_export");
     if (vkd3d_spirv_capability_is_enabled(builder, SpvCapabilityShaderViewportIndexLayerEXT))
         vkd3d_spirv_build_op_extension(&stream, "SPV_EXT_shader_viewport_index_layer");
+    if (vkd3d_spirv_capability_is_enabled(builder, SpvCapabilityDenormPreserve)
+            || vkd3d_spirv_capability_is_enabled(builder, SpvCapabilityDenormFlushToZero))
+        vkd3d_spirv_build_op_extension(&stream, "SPV_KHR_float_controls");
 
     if (builder->ext_instr_set_glsl_450)
         vkd3d_spirv_build_op_ext_inst_import(&stream, builder->ext_instr_set_glsl_450, "GLSL.std.450");
@@ -2712,7 +2719,7 @@ struct vkd3d_symbol_register_data
 
 struct vkd3d_symbol_resource_data
 {
-    struct vkd3d_shader_register_range range;
+    struct vsir_register_range range;
     enum vsir_data_type sampled_type;
     uint32_t type_id;
     const struct vkd3d_spirv_resource_type *resource_type_info;
@@ -2726,7 +2733,7 @@ struct vkd3d_symbol_resource_data
 
 struct vkd3d_symbol_sampler_data
 {
-    struct vkd3d_shader_register_range range;
+    struct vsir_register_range range;
 };
 
 struct vkd3d_descriptor_binding_address
@@ -2794,8 +2801,7 @@ static void vkd3d_symbol_free(struct rb_entry *entry, void *context)
     vkd3d_free(s);
 }
 
-static void vkd3d_symbol_make_register(struct vkd3d_symbol *symbol,
-        const struct vkd3d_shader_register *reg)
+static void vkd3d_symbol_make_register(struct vkd3d_symbol *symbol, const struct vsir_operand *reg)
 {
     symbol->type = VKD3D_SYMBOL_REGISTER;
     memset(&symbol->key, 0, sizeof(symbol->key));
@@ -2842,8 +2848,7 @@ static void vkd3d_symbol_set_register_info(struct vkd3d_symbol *symbol, uint32_t
     symbol->info.reg.is_aggregate = false;
 }
 
-static void vkd3d_symbol_make_resource(struct vkd3d_symbol *symbol,
-        const struct vkd3d_shader_register *reg)
+static void vkd3d_symbol_make_resource(struct vkd3d_symbol *symbol, const struct vsir_operand *reg)
 {
     symbol->type = VKD3D_SYMBOL_RESOURCE;
     memset(&symbol->key, 0, sizeof(symbol->key));
@@ -2851,8 +2856,7 @@ static void vkd3d_symbol_make_resource(struct vkd3d_symbol *symbol,
     symbol->key.resource.idx = reg->idx[0].offset;
 }
 
-static void vkd3d_symbol_make_sampler(struct vkd3d_symbol *symbol,
-        const struct vkd3d_shader_register *reg)
+static void vkd3d_symbol_make_sampler(struct vkd3d_symbol *symbol, const struct vsir_operand *reg)
 {
     symbol->type = VKD3D_SYMBOL_SAMPLER;
     memset(&symbol->key, 0, sizeof(symbol->key));
@@ -2860,7 +2864,7 @@ static void vkd3d_symbol_make_sampler(struct vkd3d_symbol *symbol,
 }
 
 static void vkd3d_symbol_make_combined_sampler(struct vkd3d_symbol *symbol,
-        const struct vkd3d_shader_register *resource_reg, unsigned int sampler_space, unsigned int sampler_index)
+        const struct vsir_operand *resource_reg, unsigned int sampler_space, unsigned int sampler_index)
 {
     symbol->type = VKD3D_SYMBOL_COMBINED_SAMPLER;
     memset(&symbol->key, 0, sizeof(symbol->key));
@@ -2900,7 +2904,7 @@ static const char *debug_vkd3d_symbol(const struct vkd3d_symbol *symbol)
 
 struct vkd3d_push_constant_buffer_binding
 {
-    struct vkd3d_shader_register reg;
+    struct vsir_operand reg;
     struct vkd3d_shader_push_constant_buffer pc;
     unsigned int size;
 };
@@ -3155,6 +3159,9 @@ static struct spirv_compiler *spirv_compiler_create(struct vsir_program *program
             case VKD3D_SHADER_COMPILE_OPTION_CHILD_EFFECT:
             case VKD3D_SHADER_COMPILE_OPTION_WARN_IMPLICIT_TRUNCATION:
             case VKD3D_SHADER_COMPILE_OPTION_INCLUDE_EMPTY_BUFFERS_IN_EFFECTS:
+            case VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F16:
+            case VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F32:
+            case VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F64:
                 /* Explicitly ignored for this target. */
                 break;
 
@@ -3280,7 +3287,7 @@ static bool spirv_compiler_check_shader_visibility(const struct spirv_compiler *
 }
 
 static struct vkd3d_push_constant_buffer_binding *spirv_compiler_find_push_constant_buffer(
-        const struct spirv_compiler *compiler, const struct vkd3d_shader_register_range *range)
+        const struct spirv_compiler *compiler, const struct vsir_register_range *range)
 {
     unsigned int register_space = range->space;
     unsigned int reg_idx = range->first;
@@ -3304,7 +3311,7 @@ static struct vkd3d_push_constant_buffer_binding *spirv_compiler_find_push_const
 }
 
 static bool spirv_compiler_has_combined_sampler_for_resource(const struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register_range *range)
+        const struct vsir_register_range *range)
 {
     const struct vkd3d_shader_interface_info *shader_interface = &compiler->shader_interface;
     const struct vkd3d_shader_combined_resource_sampler *combined_sampler;
@@ -3332,7 +3339,7 @@ static bool spirv_compiler_has_combined_sampler_for_resource(const struct spirv_
 }
 
 static bool spirv_compiler_has_combined_sampler_for_sampler(const struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register_range *range)
+        const struct vsir_register_range *range)
 {
     const struct vkd3d_shader_interface_info *shader_interface = &compiler->shader_interface;
     const struct vkd3d_shader_combined_resource_sampler *combined_sampler;
@@ -3359,29 +3366,33 @@ static bool spirv_compiler_has_combined_sampler_for_sampler(const struct spirv_c
     return false;
 }
 
-static void VKD3D_PRINTF_FUNC(3, 4) spirv_compiler_error(struct spirv_compiler *compiler,
-        enum vkd3d_shader_error error, const char *format, ...)
+#define spirv_compiler_error(compiler, error, ...) \
+        spirv_compiler_error_(compiler, error, __FUNCTION__, __VA_ARGS__)
+static void VKD3D_PRINTF_FUNC(4, 5) spirv_compiler_error_(struct spirv_compiler *compiler,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_verror(compiler->message_context, &compiler->location, error, format, args);
+    vkd3d_shader_verror(compiler->message_context, &compiler->location, error, function, format, args);
     va_end(args);
     compiler->failed = true;
 }
 
-static void VKD3D_PRINTF_FUNC(3, 4) spirv_compiler_warning(struct spirv_compiler *compiler,
-        enum vkd3d_shader_error error, const char *format, ...)
+#define spirv_compiler_warning(compiler, error, ...) \
+        spirv_compiler_warning_(compiler, error, __FUNCTION__, __VA_ARGS__)
+static void VKD3D_PRINTF_FUNC(4, 5) spirv_compiler_warning_(struct spirv_compiler *compiler,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_vwarning(compiler->message_context, &compiler->location, error, format, args);
+    vkd3d_shader_vwarning(compiler->message_context, &compiler->location, error, function, format, args);
     va_end(args);
 }
 
-static struct vkd3d_string_buffer *vkd3d_shader_register_range_string(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register_range *range)
+static struct vkd3d_string_buffer *vsir_register_range_string(struct spirv_compiler *compiler,
+        const struct vsir_register_range *range)
 {
     struct vkd3d_string_buffer *buffer = vkd3d_string_buffer_get(&compiler->string_buffers);
 
@@ -3473,8 +3484,8 @@ static uint32_t spirv_compiler_get_label_id(struct spirv_compiler *compiler, uns
 }
 
 static struct vkd3d_shader_descriptor_binding spirv_compiler_get_descriptor_binding(
-        struct spirv_compiler *compiler, const struct vkd3d_shader_register *reg,
-        const struct vkd3d_shader_register_range *range, enum vkd3d_shader_resource_type resource_type,
+        struct spirv_compiler *compiler, const struct vsir_operand *reg,
+        const struct vsir_register_range *range, enum vkd3d_shader_resource_type resource_type,
         bool is_uav_counter, struct vkd3d_descriptor_binding_address *binding_address)
 {
     const struct vkd3d_shader_interface_info *shader_interface = &compiler->shader_interface;
@@ -3495,7 +3506,6 @@ static struct vkd3d_shader_descriptor_binding spirv_compiler_get_descriptor_bind
         descriptor_type = VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER;
     else
     {
-        FIXME("Unhandled register type %#x.\n", reg->type);
         spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_INVALID_REGISTER_TYPE,
                 "Encountered invalid/unhandled register type %#x.", reg->type);
         goto done;
@@ -3524,12 +3534,9 @@ static struct vkd3d_shader_descriptor_binding spirv_compiler_get_descriptor_bind
                 continue;
 
             if (current->offset)
-            {
-                FIXME("Atomic counter offsets are not supported yet.\n");
                 spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_INVALID_DESCRIPTOR_BINDING,
                         "Descriptor binding for UAV counter %u, space %u has unsupported ‘offset’ %u.",
                         range->first, range->space, current->offset);
-            }
 
             binding_address->binding_base_idx = current->register_index
                     - (binding_offsets ? binding_offsets[i].static_offset : 0);
@@ -3537,11 +3544,8 @@ static struct vkd3d_shader_descriptor_binding spirv_compiler_get_descriptor_bind
             return current->binding;
         }
         if (shader_interface->uav_counter_count)
-        {
-            FIXME("Could not find descriptor binding for UAV counter %u, space %u.\n", range->first, range->space);
             spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_DESCRIPTOR_BINDING_NOT_FOUND,
                     "Could not find descriptor binding for UAV counter %u, space %u.", range->first, range->space);
-        }
     }
     else
     {
@@ -3568,10 +3572,9 @@ static struct vkd3d_shader_descriptor_binding spirv_compiler_get_descriptor_bind
         }
         if (shader_interface->binding_count)
         {
-            struct vkd3d_string_buffer *buffer = vkd3d_shader_register_range_string(compiler, range);
+            struct vkd3d_string_buffer *buffer = vsir_register_range_string(compiler, range);
             const char *range_str = buffer ? buffer->buffer : "";
-            FIXME("Could not find descriptor binding for type %#x, space %u, registers %s, shader type %#x.\n",
-                    descriptor_type, range->space, range_str, compiler->shader_type);
+
             spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_DESCRIPTOR_BINDING_NOT_FOUND,
                     "Could not find descriptor binding for type %#x, space %u, registers %s, shader type %#x.",
                     descriptor_type, range->space, range_str, compiler->shader_type);
@@ -3746,8 +3749,8 @@ static uint32_t spirv_compiler_get_constant_uint64_vector(struct spirv_compiler
     return spirv_compiler_get_constant64(compiler, VSIR_DATA_U64, component_count, values);
 }
 
-static uint32_t spirv_compiler_get_type_id_for_reg(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, uint32_t write_mask)
+static uint32_t spirv_compiler_get_type_id_for_operand(struct spirv_compiler *compiler,
+        const struct vsir_operand *reg, uint32_t write_mask)
 {
     return spirv_get_type_id(compiler, reg->data_type, vsir_write_mask_component_count(write_mask));
 }
@@ -3755,11 +3758,10 @@ static uint32_t spirv_compiler_get_type_id_for_reg(struct spirv_compiler *compil
 static uint32_t spirv_compiler_get_type_id_for_dst(struct spirv_compiler *compiler,
         const struct vsir_dst_operand *dst)
 {
-    return spirv_compiler_get_type_id_for_reg(compiler, &dst->reg, dst->write_mask);
+    return spirv_compiler_get_type_id_for_operand(compiler, &dst->reg, dst->write_mask);
 }
 
-static bool spirv_compiler_get_register_name(char *buffer, unsigned int buffer_size,
-        const struct vkd3d_shader_register *reg)
+static bool spirv_compiler_get_register_name(char *buffer, unsigned int buffer_size, const struct vsir_operand *reg)
 {
     unsigned int idx;
 
@@ -3860,7 +3862,7 @@ static bool spirv_compiler_get_register_name(char *buffer, unsigned int buffer_s
 
 /* TODO: UAV counters: vkd3d_spirv_build_op_name(builder, counter_var_id, "u%u_counter", reg->idx[0].offset); */
 static void spirv_compiler_emit_register_debug_name(struct vkd3d_spirv_builder *builder,
-        uint32_t id, const struct vkd3d_shader_register *reg)
+        uint32_t id, const struct vsir_operand *reg)
 {
     char debug_name[256];
     if (spirv_compiler_get_register_name(debug_name, ARRAY_SIZE(debug_name), reg))
@@ -4109,7 +4111,7 @@ static uint32_t spirv_compiler_emit_load_src(struct spirv_compiler *compiler,
         const struct vsir_src_operand *src, uint32_t write_mask);
 
 static uint32_t spirv_compiler_emit_register_addressing(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register_index *reg_index)
+        const struct vsir_register_index *reg_index)
 {
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
     uint32_t type_id, addr_id;
@@ -4142,12 +4144,12 @@ struct vkd3d_shader_register_info
 };
 
 static bool spirv_compiler_get_register_info(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, struct vkd3d_shader_register_info *register_info)
+        const struct vsir_operand *reg, struct vkd3d_shader_register_info *register_info)
 {
     struct vkd3d_symbol reg_symbol, *symbol;
     struct rb_entry *entry;
 
-    VKD3D_ASSERT(!register_is_constant_or_undef(reg));
+    VKD3D_ASSERT(!vsir_operand_is_constant_or_undef(reg));
 
     if (reg->type == VKD3DSPR_TEMP)
     {
@@ -4243,24 +4245,19 @@ static bool spirv_compiler_enable_descriptor_indexing(struct spirv_compiler *com
 }
 
 static uint32_t spirv_compiler_get_descriptor_index(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, const struct vkd3d_symbol *array_symbol,
+        const struct vsir_operand *reg, const struct vkd3d_symbol *array_symbol,
         unsigned int binding_base_idx, enum vkd3d_shader_resource_type resource_type)
 {
     const struct vkd3d_symbol_descriptor_array *array_key = &array_symbol->key.descriptor_array;
-    struct vkd3d_shader_register_index index = reg->idx[1];
+    struct vsir_register_index index = reg->idx[1];
     unsigned int push_constant_index;
     uint32_t index_id;
 
-    if ((push_constant_index = array_key->push_constant_index) != ~0u || index.rel_addr)
-    {
-        if (!spirv_compiler_enable_descriptor_indexing(compiler, reg->type, resource_type))
-        {
-            FIXME("The target environment does not support descriptor indexing.\n");
-            spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_DESCRIPTOR_IDX_UNSUPPORTED,
-                    "Cannot dynamically index a descriptor array of type %#x, id %u. "
-                    "The target environment does not support descriptor indexing.", reg->type, reg->idx[0].offset);
-        }
-    }
+    if (((push_constant_index = array_key->push_constant_index) != ~0u || index.rel_addr)
+            && !spirv_compiler_enable_descriptor_indexing(compiler, reg->type, resource_type))
+        spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_DESCRIPTOR_IDX_UNSUPPORTED,
+                "Cannot dynamically index a descriptor array of type %#x, id %u. "
+                "The target environment does not support descriptor indexing.", reg->type, reg->idx[0].offset);
 
     index.offset -= binding_base_idx;
     index_id = spirv_compiler_emit_register_addressing(compiler, &index);
@@ -4291,7 +4288,7 @@ static uint32_t spirv_compiler_get_descriptor_index(struct spirv_compiler *compi
 }
 
 static void spirv_compiler_emit_dereference_register(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, struct vkd3d_shader_register_info *register_info)
+        const struct vsir_operand *reg, struct vkd3d_shader_register_info *register_info)
 {
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
     unsigned int component_count, index_count = 0;
@@ -4332,7 +4329,7 @@ static void spirv_compiler_emit_dereference_register(struct spirv_compiler *comp
             FIXME("Relative addressing not implemented.\n");
 
         /* Handle arrayed registers, e.g. v[3][0]. */
-        if (reg->idx_count > 1 && !vsir_register_is_descriptor(reg))
+        if (reg->idx_count > 1 && !vsir_operand_is_descriptor(reg))
             indexes[index_count++] = spirv_compiler_emit_register_addressing(compiler, &reg->idx[0]);
     }
 
@@ -4352,8 +4349,7 @@ static void spirv_compiler_emit_dereference_register(struct spirv_compiler *comp
     }
 }
 
-static uint32_t spirv_compiler_get_register_id(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg)
+static uint32_t spirv_compiler_get_register_id(struct spirv_compiler *compiler, const struct vsir_operand *reg)
 {
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
     struct vkd3d_shader_register_info register_info;
@@ -4373,7 +4369,7 @@ static bool vkd3d_swizzle_is_equal(uint32_t dst_write_mask, uint32_t swizzle, ui
     return vkd3d_compact_swizzle(VKD3D_SHADER_NO_SWIZZLE, dst_write_mask) == vkd3d_compact_swizzle(swizzle, write_mask);
 }
 
-static bool vkd3d_swizzle_is_scalar(uint32_t swizzle, const struct vkd3d_shader_register *reg)
+static bool vkd3d_swizzle_is_scalar(uint32_t swizzle, const struct vsir_operand *reg)
 {
     unsigned int component_idx = vsir_swizzle_get_component(swizzle, 0);
 
@@ -4531,7 +4527,7 @@ static uint32_t spirv_compiler_emit_bool_to_double(struct spirv_compiler *compil
 }
 
 static uint32_t spirv_compiler_emit_load_constant(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, uint32_t swizzle, uint32_t write_mask)
+        const struct vsir_operand *reg, uint32_t swizzle, uint32_t write_mask)
 {
     unsigned int component_count = vsir_write_mask_component_count(write_mask);
     uint32_t values[VKD3D_VEC4_SIZE] = {0};
@@ -4557,7 +4553,7 @@ static uint32_t spirv_compiler_emit_load_constant(struct spirv_compiler *compile
 }
 
 static uint32_t spirv_compiler_emit_load_constant64(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, uint32_t swizzle, uint32_t write_mask)
+        const struct vsir_operand *reg, uint32_t swizzle, uint32_t write_mask)
 {
     unsigned int component_count = vsir_write_mask_component_count(write_mask);
     uint64_t values[VKD3D_DVEC2_SIZE] = {0};
@@ -4583,7 +4579,7 @@ static uint32_t spirv_compiler_emit_load_constant64(struct spirv_compiler *compi
 }
 
 static uint32_t spirv_compiler_emit_load_undef(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, uint32_t write_mask)
+        const struct vsir_operand *reg, uint32_t write_mask)
 {
     unsigned int component_count = vsir_write_mask_component_count(write_mask);
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
@@ -4596,16 +4592,15 @@ static uint32_t spirv_compiler_emit_load_undef(struct spirv_compiler *compiler,
     return vkd3d_spirv_get_op_undef(builder, type_id);
 }
 
-static uint32_t spirv_compiler_emit_load_scalar(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, uint32_t swizzle,
-        uint32_t write_mask, const struct vkd3d_shader_register_info *reg_info)
+static uint32_t spirv_compiler_emit_load_scalar(struct spirv_compiler *compiler, const struct vsir_operand *reg,
+        uint32_t swizzle, uint32_t write_mask, const struct vkd3d_shader_register_info *reg_info)
 {
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
     uint32_t type_id, ptr_type_id, index, reg_id, val_id;
     unsigned int component_idx, reg_component_count;
     uint32_t skipped_component_mask;
 
-    VKD3D_ASSERT(!register_is_constant_or_undef(reg));
+    VKD3D_ASSERT(!vsir_operand_is_constant_or_undef(reg));
     VKD3D_ASSERT(vsir_write_mask_component_count(write_mask) == 1);
 
     component_idx = vsir_write_mask_get_component_idx(write_mask);
@@ -4716,7 +4711,7 @@ static uint32_t spirv_compiler_emit_constant_array(struct spirv_compiler *compil
 }
 
 static void spirv_compiler_set_ssa_register_info(const struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, uint32_t write_mask, uint32_t val_id)
+        const struct vsir_operand *reg, uint32_t write_mask, uint32_t val_id)
 {
     unsigned int i = reg->idx[0].offset;
     VKD3D_ASSERT(i < compiler->ssa_register_count);
@@ -4726,7 +4721,7 @@ static void spirv_compiler_set_ssa_register_info(const struct spirv_compiler *co
 }
 
 static uint32_t spirv_compiler_emit_load_reg(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, uint32_t swizzle, uint32_t write_mask)
+        const struct vsir_operand *reg, uint32_t swizzle, uint32_t write_mask)
 {
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
     struct vkd3d_shader_register_info reg_info;
@@ -4905,7 +4900,7 @@ static void spirv_compiler_emit_store(struct spirv_compiler *compiler, uint32_t
 }
 
 static void spirv_compiler_emit_store_reg(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, uint32_t write_mask, uint32_t val_id)
+        const struct vsir_operand *reg, uint32_t write_mask, uint32_t val_id)
 {
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
     struct vkd3d_shader_register_info reg_info;
@@ -4913,7 +4908,7 @@ static void spirv_compiler_emit_store_reg(struct spirv_compiler *compiler,
     enum vsir_data_type data_type;
     uint32_t type_id;
 
-    VKD3D_ASSERT(!register_is_constant_or_undef(reg));
+    VKD3D_ASSERT(!vsir_operand_is_constant_or_undef(reg));
 
     if (reg->type == VKD3DSPR_SSA)
     {
@@ -4944,7 +4939,7 @@ static void spirv_compiler_emit_store_reg(struct spirv_compiler *compiler,
 }
 
 static uint32_t spirv_compiler_emit_sat(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, uint32_t write_mask, uint32_t val_id)
+        const struct vsir_operand *reg, uint32_t write_mask, uint32_t val_id)
 {
     unsigned int component_count = vsir_write_mask_component_count(write_mask);
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
@@ -4961,7 +4956,7 @@ static uint32_t spirv_compiler_emit_sat(struct spirv_compiler *compiler,
         one_id = spirv_compiler_get_constant_float_vector(compiler, 1.0f, component_count);
     }
 
-    type_id = spirv_compiler_get_type_id_for_reg(compiler, reg, write_mask);
+    type_id = spirv_compiler_get_type_id_for_operand(compiler, reg, write_mask);
     if (data_type_is_floating_point(reg->data_type))
         return vkd3d_spirv_build_op_glsl_std450_nclamp(builder, type_id, val_id, zero_id, one_id);
 
@@ -5052,12 +5047,9 @@ static void spirv_compiler_decorate_builtin(struct spirv_compiler *compiler,
                 case VKD3D_SHADER_TYPE_DOMAIN:
                     if (!spirv_compiler_is_target_extension_supported(compiler,
                             VKD3D_SHADER_SPIRV_EXTENSION_EXT_VIEWPORT_INDEX_LAYER))
-                    {
-                        FIXME("The target environment does not support decoration Layer.\n");
                         spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
                                 "Cannot use SV_RenderTargetArrayIndex. "
                                 "The target environment does not support decoration Layer.");
-                    }
                     vkd3d_spirv_enable_capability(builder, SpvCapabilityShaderViewportIndexLayerEXT);
                     break;
 
@@ -5079,12 +5071,9 @@ static void spirv_compiler_decorate_builtin(struct spirv_compiler *compiler,
                 case VKD3D_SHADER_TYPE_DOMAIN:
                     if (!spirv_compiler_is_target_extension_supported(compiler,
                             VKD3D_SHADER_SPIRV_EXTENSION_EXT_VIEWPORT_INDEX_LAYER))
-                    {
-                        FIXME("The target environment does not support decoration ViewportIndex.\n");
                         spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
                                 "Cannot use SV_ViewportArrayIndex. "
                                 "The target environment does not support decoration ViewportIndex.");
-                    }
                     vkd3d_spirv_enable_capability(builder, SpvCapabilityShaderViewportIndexLayerEXT);
                     break;
 
@@ -5320,12 +5309,9 @@ static void spirv_compiler_emit_register_execution_mode(struct spirv_compiler *c
         case VKD3DSPR_OUTSTENCILREF:
             if (!spirv_compiler_is_target_extension_supported(compiler,
                     VKD3D_SHADER_SPIRV_EXTENSION_EXT_STENCIL_EXPORT))
-            {
-                FIXME("The target environment does not support stencil export.\n");
                 spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
                         "Cannot export stencil reference value. "
                         "The target environment does not support stencil export.");
-            }
             vkd3d_spirv_enable_capability(&compiler->spirv_builder, SpvCapabilityStencilExportEXT);
             spirv_compiler_emit_execution_mode(compiler, SpvExecutionModeStencilRefReplacingEXT, NULL, 0);
             break;
@@ -5399,11 +5385,11 @@ static const struct vkd3d_spirv_builtin *vkd3d_get_spirv_builtin(const struct sp
 
 static uint32_t spirv_compiler_get_invocation_id(struct spirv_compiler *compiler)
 {
-    struct vkd3d_shader_register r;
+    struct vsir_operand r;
 
     VKD3D_ASSERT(compiler->shader_type == VKD3D_SHADER_TYPE_HULL);
 
-    vsir_register_init(&r, VKD3DSPR_OUTPOINTID, VSIR_DATA_F32, 0);
+    vsir_operand_init(&r, VKD3DSPR_OUTPOINTID, VSIR_DATA_F32, 0);
     return spirv_compiler_get_register_id(compiler, &r);
 }
 
@@ -5555,7 +5541,7 @@ static const struct vkd3d_symbol *spirv_compiler_emit_io_register(struct spirv_c
         const struct vsir_dst_operand *dst)
 {
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
-    const struct vkd3d_shader_register *reg = &dst->reg;
+    const struct vsir_operand *reg = &dst->reg;
     const struct vkd3d_spirv_builtin *builtin;
     struct vkd3d_symbol reg_symbol;
     SpvStorageClass storage_class;
@@ -5736,9 +5722,9 @@ static void spirv_compiler_emit_input(struct spirv_compiler *compiler,
 
     if (use_private_var)
     {
-        struct vkd3d_shader_register dst_reg;
+        struct vsir_operand dst_reg;
 
-        vsir_register_init(&dst_reg, reg_type, VSIR_DATA_F32, 1);
+        vsir_operand_init(&dst_reg, reg_type, VSIR_DATA_F32, 1);
         dst_reg.idx[0].offset = element_idx;
 
         type_id = spirv_get_type_id(compiler, data_type, input_component_count);
@@ -6156,23 +6142,71 @@ static size_t spirv_compiler_get_current_function_location(struct spirv_compiler
     return builder->main_function_location;
 }
 
+static void spirv_compiler_emit_denormal_mode(struct spirv_compiler *compiler,
+        unsigned int bit_width, enum vkd3d_shader_denormal_mode mode)
+{
+    if (mode == VKD3D_SHADER_DENORMAL_MODE_ANY)
+        return;
+
+    if (!spirv_compiler_is_target_extension_supported(compiler,
+            VKD3D_SHADER_SPIRV_EXTENSION_KHR_FLOAT_CONTROLS))
+    {
+        spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
+                "Cannot emit denormal mode for %u-bit floats. "
+                "The target environment does not support float controls.", bit_width);
+        return;
+    }
+
+    if (compiler->program->f16_denormal_mode == VKD3D_SHADER_DENORMAL_MODE_PRESERVE)
+    {
+        vkd3d_spirv_enable_capability(&compiler->spirv_builder, SpvCapabilityDenormPreserve);
+        spirv_compiler_emit_execution_mode(compiler, SpvExecutionModeDenormPreserve, &bit_width, 1);
+    }
+    else if (compiler->program->f16_denormal_mode == VKD3D_SHADER_DENORMAL_MODE_FLUSH_TO_ZERO)
+    {
+        vkd3d_spirv_enable_capability(&compiler->spirv_builder, SpvCapabilityDenormFlushToZero);
+        spirv_compiler_emit_execution_mode(compiler, SpvExecutionModeDenormFlushToZero, &bit_width, 1);
+    }
+}
+
 static void spirv_compiler_emit_global_flags(struct spirv_compiler *compiler, enum vsir_global_flags flags)
 {
+    static const uint64_t ignored_flags = VKD3DSGF_REFACTORING_ALLOWED
+            | VKD3DSGF_ENABLE_RAW_AND_STRUCTURED_BUFFERS
+            | VKD3DSGF_SKIP_OPTIMIZATION
+            | VKD3DSGF_BIND_FOR_DURATION
+            | VKD3DSGF_ENABLE_VP_AND_RT_ARRAY_INDEX
+            | VKD3DSGF_ENABLE_STENCIL_REF
+            | VKD3DSGF_ENABLE_UP_TO_64_UAVS
+            | VKD3DSGF_ENABLE_UAVS_AT_EVERY_STAGE
+            | VKD3DSGF_ENABLE_RASTERIZER_ORDERED_VIEWS;
+
     if (flags & VKD3DSGF_FORCE_EARLY_DEPTH_STENCIL)
     {
         spirv_compiler_emit_execution_mode(compiler, SpvExecutionModeEarlyFragmentTests, NULL, 0);
         flags &= ~VKD3DSGF_FORCE_EARLY_DEPTH_STENCIL;
     }
 
+    /* We're free to ignore this. There may be performance advantages to using
+     * 16-bit operations for minimum precision types when supported, but in
+     * terms of correctness the current behaviour should be fine. */
+    if (flags & VKD3DSGF_ENABLE_MINIMUM_PRECISION)
+    {
+        WARN("Ignoring the \"enableMinimumPrecision\" global flag.\n");
+        flags &= ~VKD3DSGF_ENABLE_MINIMUM_PRECISION;
+    }
+
+    spirv_compiler_emit_denormal_mode(compiler, 32, compiler->program->f32_denormal_mode);
+
     if (flags & (VKD3DSGF_ENABLE_DOUBLE_PRECISION_FLOAT_OPS | VKD3DSGF_ENABLE_11_1_DOUBLE_EXTENSIONS))
     {
         if (compiler->features & VKD3D_SHADER_COMPILE_OPTION_FEATURE_FLOAT64)
         {
             vkd3d_spirv_enable_capability(&compiler->spirv_builder, SpvCapabilityFloat64);
+            spirv_compiler_emit_denormal_mode(compiler, 64, compiler->program->f64_denormal_mode);
         }
         else
         {
-            WARN("Unsupported 64-bit float ops.\n");
             spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
                     "The target environment does not support 64-bit floating point.");
         }
@@ -6182,39 +6216,41 @@ static void spirv_compiler_emit_global_flags(struct spirv_compiler *compiler, en
     if (flags & VKD3DSGF_ENABLE_INT64)
     {
         if (compiler->features & VKD3D_SHADER_COMPILE_OPTION_FEATURE_INT64)
-        {
             vkd3d_spirv_enable_capability(&compiler->spirv_builder, SpvCapabilityInt64);
-        }
         else
-        {
-            WARN("Unsupported 64-bit integer ops.\n");
             spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
                     "The target environment does not support 64-bit integers.");
-        }
         flags &= ~VKD3DSGF_ENABLE_INT64;
     }
 
+    if (flags & VKD3DSGF_ENABLE_RELAXED_TYPED_UAV_FORMATS)
+    {
+        if (!compiler->uav_read_without_format)
+            spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
+                    "The target environment does not support accessing storage images without specifying a format.");
+        flags &= ~VKD3DSGF_ENABLE_RELAXED_TYPED_UAV_FORMATS;
+    }
+
     if (flags & VKD3DSGF_ENABLE_WAVE_INTRINSICS)
     {
         if (!(compiler->features & VKD3D_SHADER_COMPILE_OPTION_FEATURE_WAVE_OPS))
-        {
-            WARN("Unsupported wave ops.\n");
             spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
                     "The target environment does not support wave ops.");
-        }
         else if (!spirv_compiler_is_spirv_min_1_3_target(compiler))
-        {
-            WARN("Wave ops enabled but environment does not support SPIR-V 1.3 or greater.\n");
             spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
                     "The target environment uses wave ops but does not support SPIR-V 1.3 or greater.");
-        }
         flags &= ~VKD3DSGF_ENABLE_WAVE_INTRINSICS;
     }
 
-    if (flags & ~(VKD3DSGF_REFACTORING_ALLOWED | VKD3DSGF_ENABLE_RAW_AND_STRUCTURED_BUFFERS))
-        FIXME("Unhandled global flags %#"PRIx64".\n", (uint64_t)flags);
-    else if (flags)
-        WARN("Unhandled global flags %#"PRIx64".\n", (uint64_t)flags);
+    if (flags & ignored_flags)
+    {
+        TRACE("Ignoring global flags %#"PRIx64".\n", flags & ignored_flags);
+        flags &= ~ignored_flags;
+    }
+
+    if (flags)
+        spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_NOT_IMPLEMENTED,
+                "Unhandled global flags %#"PRIx64".", (uint64_t)flags);
 }
 
 static void spirv_compiler_emit_temps(struct spirv_compiler *compiler, uint32_t count)
@@ -6247,11 +6283,8 @@ static void spirv_compiler_allocate_ssa_register_ids(struct spirv_compiler *comp
 {
     VKD3D_ASSERT(!compiler->ssa_register_info);
     if (!(compiler->ssa_register_info = vkd3d_calloc(count, sizeof(*compiler->ssa_register_info))))
-    {
-        ERR("Failed to allocate SSA register value id array, count %u.\n", count);
         spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_OUT_OF_MEMORY,
                 "Failed to allocate SSA register value id array of count %u.", count);
-    }
     compiler->ssa_register_count = count;
 }
 
@@ -6261,17 +6294,17 @@ static void spirv_compiler_emit_dcl_indexable_temp(struct spirv_compiler *compil
     const struct vkd3d_shader_indexable_temp *temp = &instruction->declaration.indexable_temp;
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
     uint32_t id, type_id, length_id, ptr_type_id, init_id = 0;
-    struct vkd3d_shader_register reg;
     struct vkd3d_symbol reg_symbol;
     SpvStorageClass storage_class;
     size_t function_location;
+    struct vsir_operand reg;
 
     /* Indexable temps may be used by more than one function in hull shaders, and
      * declarations generally should not occur within VSIR code blocks unless function
      * scope is specified, e.g. DXIL alloca. */
     storage_class = temp->has_function_scope ? SpvStorageClassFunction : SpvStorageClassPrivate;
 
-    vsir_register_init(&reg, VKD3DSPR_IDXTEMP, VSIR_DATA_F32, 1);
+    vsir_operand_init(&reg, VKD3DSPR_IDXTEMP, VSIR_DATA_F32, 1);
     reg.idx[0].offset = temp->register_idx;
 
     /* Alignment is supported only in the Kernel execution model and is an optimisation only. */
@@ -6386,7 +6419,7 @@ static void spirv_compiler_emit_push_constant_buffers(struct spirv_compiler *com
 
 static const struct vkd3d_shader_descriptor_info1 *spirv_compiler_get_descriptor_info(
         struct spirv_compiler *compiler, enum vkd3d_shader_descriptor_type type,
-        const struct vkd3d_shader_register_range *range)
+        const struct vsir_register_range *range)
 {
     const struct vkd3d_shader_scan_descriptor_info1 *descriptor_info = &compiler->program->descriptors;
     unsigned int register_last = (range->last == ~0u) ? range->first : range->last;
@@ -6422,8 +6455,8 @@ static void spirv_compiler_decorate_descriptor(struct spirv_compiler *compiler,
 }
 
 static uint32_t spirv_compiler_build_descriptor_variable(struct spirv_compiler *compiler,
-        SpvStorageClass storage_class, uint32_t type_id, const struct vkd3d_shader_register *reg,
-        const struct vkd3d_shader_register_range *range, enum vkd3d_shader_resource_type resource_type,
+        SpvStorageClass storage_class, uint32_t type_id, const struct vsir_operand *reg,
+        const struct vsir_register_range *range, enum vkd3d_shader_resource_type resource_type,
         const struct vkd3d_shader_descriptor_info1 *descriptor, bool is_uav_counter,
         struct vkd3d_descriptor_variable_info *var_info)
 {
@@ -6497,7 +6530,7 @@ static uint32_t spirv_compiler_build_descriptor_variable(struct spirv_compiler *
 }
 
 static void spirv_compiler_emit_cbv_declaration(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register_range *range, const struct vkd3d_shader_descriptor_info1 *descriptor)
+        const struct vsir_register_range *range, const struct vkd3d_shader_descriptor_info1 *descriptor)
 {
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
     uint32_t vec4_id, array_type_id, length_id, struct_id, var_id;
@@ -6505,11 +6538,11 @@ static void spirv_compiler_emit_cbv_declaration(struct spirv_compiler *compiler,
     unsigned int size_in_bytes = descriptor->buffer_size;
     struct vkd3d_push_constant_buffer_binding *push_cb;
     struct vkd3d_descriptor_variable_info var_info;
-    struct vkd3d_shader_register reg;
     struct vkd3d_symbol reg_symbol;
+    struct vsir_operand reg;
     unsigned int size;
 
-    vsir_register_init(&reg, VKD3DSPR_CONSTBUFFER, VSIR_DATA_F32, 3);
+    vsir_operand_init(&reg, VKD3DSPR_CONSTBUFFER, VSIR_DATA_F32, 3);
     reg.idx[0].offset = descriptor->register_id;
     reg.idx[1].offset = range->first;
     reg.idx[2].offset = range->last;
@@ -6558,16 +6591,16 @@ static void spirv_compiler_emit_cbv_declaration(struct spirv_compiler *compiler,
 }
 
 static void spirv_compiler_emit_sampler_declaration(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register_range *range, const struct vkd3d_shader_descriptor_info1 *descriptor)
+        const struct vsir_register_range *range, const struct vkd3d_shader_descriptor_info1 *descriptor)
 {
     const SpvStorageClass storage_class = SpvStorageClassUniformConstant;
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
     struct vkd3d_descriptor_variable_info var_info;
-    struct vkd3d_shader_register reg;
     struct vkd3d_symbol reg_symbol;
     uint32_t type_id, var_id;
+    struct vsir_operand reg;
 
-    vsir_register_init(&reg, VKD3DSPR_SAMPLER, VSIR_DATA_F32, 1);
+    vsir_operand_init(&reg, VKD3DSPR_SAMPLER, VSIR_DATA_F32, 1);
     reg.idx[0].offset = descriptor->register_id;
 
     vkd3d_symbol_make_sampler(&reg_symbol, &reg);
@@ -6628,7 +6661,7 @@ static SpvImageFormat image_format_for_image_read(enum vsir_data_type data_type)
 }
 
 static uint32_t spirv_compiler_get_image_type_id(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, const struct vkd3d_shader_register_range *range,
+        const struct vsir_operand *reg, const struct vsir_register_range *range,
         const struct vkd3d_spirv_resource_type *resource_type_info, enum vsir_data_type data_type, bool raw_structured)
 {
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
@@ -6657,7 +6690,7 @@ static uint32_t spirv_compiler_get_image_type_id(struct spirv_compiler *compiler
 }
 
 static void spirv_compiler_emit_combined_sampler_declarations(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *resource, const struct vkd3d_shader_register_range *resource_range,
+        const struct vsir_operand *resource, const struct vsir_register_range *resource_range,
         enum vkd3d_shader_resource_type resource_type, enum vsir_data_type sampled_type,
         unsigned int structure_stride, bool raw, const struct vkd3d_spirv_resource_type *resource_type_info)
 {
@@ -6687,14 +6720,11 @@ static void spirv_compiler_emit_combined_sampler_declarations(struct spirv_compi
             continue;
 
         if (current->binding.count != 1)
-        {
-            FIXME("Descriptor arrays are not supported.\n");
             spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_INVALID_DESCRIPTOR_BINDING,
                     "Combined descriptor binding for resource %u, space %u, "
                     "and sampler %u, space %u has unsupported ‘count’ %u.",
                     resource_range->first, resource_range->space, current->sampler_index,
                     current->sampler_space, current->binding.count);
-        }
 
         image_type_id = spirv_compiler_get_image_type_id(compiler, resource, resource_range,
                 resource_type_info, sampled_type, structure_stride || raw);
@@ -6732,7 +6762,7 @@ static void spirv_compiler_emit_combined_sampler_declarations(struct spirv_compi
 }
 
 static void spirv_compiler_emit_resource_declaration(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register_range *range, const struct vkd3d_shader_descriptor_info1 *descriptor)
+        const struct vsir_register_range *range, const struct vkd3d_shader_descriptor_info1 *descriptor)
 {
     bool raw = descriptor->flags & VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_RAW_BUFFER;
     enum vkd3d_shader_resource_type resource_type = descriptor->resource_type;
@@ -6745,10 +6775,10 @@ static void spirv_compiler_emit_resource_declaration(struct spirv_compiler *comp
     const struct vkd3d_spirv_resource_type *resource_type_info;
     unsigned int sample_count = descriptor->sample_count;
     struct vkd3d_symbol resource_symbol;
-    struct vkd3d_shader_register reg;
     enum vsir_data_type sampled_type;
+    struct vsir_operand reg;
 
-    vsir_register_init(&reg, is_uav ? VKD3DSPR_UAV : VKD3DSPR_RESOURCE, VSIR_DATA_F32, 1);
+    vsir_operand_init(&reg, is_uav ? VKD3DSPR_UAV : VKD3DSPR_RESOURCE, VSIR_DATA_F32, 1);
     reg.idx[0].offset = descriptor->register_id;
 
     if (resource_type == VKD3D_SHADER_RESOURCE_TEXTURE_2DMS && sample_count == 1)
@@ -6861,9 +6891,8 @@ static void spirv_compiler_emit_resource_declaration(struct spirv_compiler *comp
     spirv_compiler_put_symbol(compiler, &resource_symbol);
 }
 
-static void spirv_compiler_emit_workgroup_memory(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *reg, unsigned int alignment, unsigned int size,
-        unsigned int structure_stride, bool zero_init)
+static void spirv_compiler_emit_workgroup_memory(struct spirv_compiler *compiler, const struct vsir_operand *reg,
+        unsigned int alignment, unsigned int size, unsigned int structure_stride, bool zero_init)
 {
     uint32_t type_id, array_type_id, length_id, pointer_type_id, var_id, init_id;
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
@@ -6871,11 +6900,8 @@ static void spirv_compiler_emit_workgroup_memory(struct spirv_compiler *compiler
     struct vkd3d_symbol reg_symbol;
 
     if (zero_init && !(compiler->features & VKD3D_SHADER_COMPILE_OPTION_FEATURE_ZERO_INITIALIZE_WORKGROUP_MEMORY))
-    {
-        WARN("Unsupported zero-initialized workgroup memory.\n");
         spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
                 "The target environment does not support zero-initialized workgroup memory.");
-    }
 
     /* Alignment is supported only in the Kernel execution model. */
     if (alignment)
@@ -7339,34 +7365,23 @@ static void spirv_compiler_emit_bool_cast(struct spirv_compiler *compiler,
 
     val_id = spirv_compiler_emit_load_src(compiler, src, dst->write_mask);
     if (dst->reg.data_type == VSIR_DATA_F16 || dst->reg.data_type == VSIR_DATA_F32)
-    {
         val_id = spirv_compiler_emit_bool_to_float(compiler, 1, val_id, instruction->opcode == VSIR_OP_ITOF);
-    }
     else if (dst->reg.data_type == VSIR_DATA_F64)
-    {
         /* ITOD is not supported. Frontends which emit bool casts must use ITOF for double. */
         val_id = spirv_compiler_emit_bool_to_double(compiler, 1, val_id, instruction->opcode == VSIR_OP_ITOF);
-    }
     else if (dst->reg.data_type == VSIR_DATA_I16 || dst->reg.data_type == VSIR_DATA_I32
             || dst->reg.data_type == VSIR_DATA_U16 || dst->reg.data_type == VSIR_DATA_U32)
-    {
         val_id = spirv_compiler_emit_bool_to_int(compiler, 1, val_id, instruction->opcode == VSIR_OP_ITOI);
-    }
     else if (dst->reg.data_type == VSIR_DATA_I64 || dst->reg.data_type == VSIR_DATA_U64)
-    {
         val_id = spirv_compiler_emit_bool_to_int64(compiler, 1, val_id, instruction->opcode == VSIR_OP_ITOI);
-    }
     else
-    {
-        WARN("Unhandled data type %u.\n", dst->reg.data_type);
         spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_INVALID_TYPE,
                 "Register data type %u is unhandled.", dst->reg.data_type);
-    }
 
     spirv_compiler_emit_store_dst(compiler, dst, val_id);
 }
 
-static enum vkd3d_result spirv_compiler_emit_alu_instruction(struct spirv_compiler *compiler,
+static void spirv_compiler_emit_alu_instruction(struct spirv_compiler *compiler,
         const struct vkd3d_shader_instruction *instruction)
 {
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
@@ -7380,10 +7395,9 @@ static enum vkd3d_result spirv_compiler_emit_alu_instruction(struct spirv_compil
     if (src->reg.data_type == VSIR_DATA_U64 && instruction->opcode == VSIR_OP_COUNTBITS)
     {
         /* At least some drivers support this anyway, but if validation is enabled it will fail. */
-        FIXME("Unsupported 64-bit source for bit count.\n");
         spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_NOT_IMPLEMENTED,
                 "64-bit source for bit count is not supported.");
-        return VKD3D_ERROR_INVALID_SHADER;
+        return;
     }
 
     if (src->reg.data_type == VSIR_DATA_BOOL)
@@ -7399,7 +7413,7 @@ static enum vkd3d_result spirv_compiler_emit_alu_instruction(struct spirv_compil
             /* VSIR supports cast from bool to signed/unsigned integer types and floating point types,
              * where bool is treated as a 1-bit integer and a signed 'true' value converts to -1. */
             spirv_compiler_emit_bool_cast(compiler, instruction);
-            return VKD3D_OK;
+            return;
         }
     }
     else
@@ -7412,7 +7426,7 @@ static enum vkd3d_result spirv_compiler_emit_alu_instruction(struct spirv_compil
         spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_INVALID_HANDLER,
                 "Encountered invalid/unhandled instruction \"%s\" (%#x).",
                 vsir_opcode_get_name(instruction->opcode, "<unknown>"), instruction->opcode);
-        return VKD3D_ERROR_INVALID_SHADER;
+        return;
     }
 
     VKD3D_ASSERT(instruction->dst_count == 1);
@@ -7444,7 +7458,6 @@ static enum vkd3d_result spirv_compiler_emit_alu_instruction(struct spirv_compil
         vkd3d_spirv_build_op_decorate(builder, val_id, SpvDecorationNoContraction, NULL, 0);
 
     spirv_compiler_emit_store_dst(compiler, dst, val_id);
-    return VKD3D_OK;
 }
 
 static void spirv_compiler_emit_saturate(struct spirv_compiler *compiler,
@@ -7600,7 +7613,7 @@ static void spirv_compiler_emit_mov(struct spirv_compiler *compiler,
     unsigned int i, component_count, write_mask;
     uint32_t components[VKD3D_VEC4_SIZE];
 
-    if (register_is_constant_or_undef(&src->reg) || src->reg.type == VKD3DSPR_SSA || dst->reg.type == VKD3DSPR_SSA
+    if (vsir_operand_is_constant_or_undef(&src->reg) || src->reg.type == VKD3DSPR_SSA || dst->reg.type == VKD3DSPR_SSA
             || src->reg.type == VKD3DSPR_PARAMETER || dst->modifiers || src->modifiers)
         goto general_implementation;
 
@@ -7808,7 +7821,7 @@ static void spirv_compiler_emit_ftoi(struct spirv_compiler *compiler,
         float_max_id = spirv_compiler_get_constant_float_vector(compiler, 2147483648.0f, component_count);
     }
 
-    src_type_id = spirv_compiler_get_type_id_for_reg(compiler, &src->reg, write_mask);
+    src_type_id = spirv_compiler_get_type_id_for_operand(compiler, &src->reg, write_mask);
     dst_type_id = spirv_compiler_get_type_id_for_dst(compiler, dst);
     src_id = spirv_compiler_emit_load_src(compiler, src, write_mask);
     val_id = vkd3d_spirv_build_op_glsl_std450_max(builder, src_type_id, src_id, int_min_id);
@@ -7863,7 +7876,7 @@ static void spirv_compiler_emit_ftou(struct spirv_compiler *compiler,
         float_max_id = spirv_compiler_get_constant_float_vector(compiler, 4294967296.0f, component_count);
     }
 
-    src_type_id = spirv_compiler_get_type_id_for_reg(compiler, &src->reg, write_mask);
+    src_type_id = spirv_compiler_get_type_id_for_operand(compiler, &src->reg, write_mask);
     dst_type_id = spirv_compiler_get_type_id_for_dst(compiler, dst);
     src_id = spirv_compiler_emit_load_src(compiler, src, write_mask);
     val_id = vkd3d_spirv_build_op_glsl_std450_max(builder, src_type_id, src_id, zero_id);
@@ -8336,7 +8349,7 @@ static void spirv_compiler_emit_branch(struct spirv_compiler *compiler,
     const struct vsir_src_operand *src = instruction->src;
     uint32_t condition_id;
 
-    if (vsir_register_is_label(&src[0].reg))
+    if (vsir_operand_is_label(&src[0].reg))
     {
         if (instruction->src_count > 1)
         {
@@ -8351,11 +8364,8 @@ static void spirv_compiler_emit_branch(struct spirv_compiler *compiler,
     }
 
     if (!vkd3d_swizzle_is_scalar(src->swizzle, &src->reg))
-    {
-        WARN("Unexpected src swizzle %#x.\n", src->swizzle);
         spirv_compiler_warning(compiler, VKD3D_SHADER_WARNING_SPV_INVALID_SWIZZLE,
                 "The swizzle for a branch condition value is not scalar.");
-    }
 
     condition_id = spirv_compiler_emit_load_src(compiler, &src[0], VKD3DSP_WRITEMASK_0);
     if (src[0].reg.data_type != VSIR_DATA_BOOL)
@@ -8382,11 +8392,8 @@ static void spirv_compiler_emit_switch(struct spirv_compiler *compiler,
     uint32_t *cases;
 
     if (!vkd3d_swizzle_is_scalar(src[0].swizzle, &src[0].reg))
-    {
-        WARN("Unexpected src swizzle %#x.\n", src[0].swizzle);
         spirv_compiler_warning(compiler, VKD3D_SHADER_WARNING_SPV_INVALID_SWIZZLE,
                 "The swizzle for a switch value is not scalar.");
-    }
 
     word_count = instruction->src_count - 3;
     if (!(cases = vkd3d_calloc(word_count, sizeof(*cases))))
@@ -8487,7 +8494,7 @@ struct vkd3d_shader_image
 #define VKD3D_IMAGE_FLAG_SAMPLED 0x4
 
 static const struct vkd3d_symbol *spirv_compiler_find_resource(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *resource_reg)
+        const struct vsir_operand *resource_reg)
 {
     struct vkd3d_symbol resource_key;
     struct rb_entry *entry;
@@ -8499,7 +8506,7 @@ static const struct vkd3d_symbol *spirv_compiler_find_resource(struct spirv_comp
 }
 
 static const struct vkd3d_symbol *spirv_compiler_find_combined_sampler(struct spirv_compiler *compiler,
-        const struct vkd3d_shader_register *resource_reg, const struct vkd3d_shader_register *sampler_reg)
+        const struct vsir_operand *resource_reg, const struct vsir_operand *sampler_reg)
 {
     const struct vkd3d_shader_interface_info *shader_interface = &compiler->shader_interface;
     unsigned int sampler_space, sampler_index;
@@ -8532,9 +8539,8 @@ static const struct vkd3d_symbol *spirv_compiler_find_combined_sampler(struct sp
     return NULL;
 }
 
-static void spirv_compiler_prepare_image(struct spirv_compiler *compiler,
-        struct vkd3d_shader_image *image, const struct vkd3d_shader_register *resource_reg,
-        const struct vkd3d_shader_register *sampler_reg, unsigned int flags)
+static void spirv_compiler_prepare_image(struct spirv_compiler *compiler, struct vkd3d_shader_image *image,
+        const struct vsir_operand *resource_reg, const struct vsir_operand *sampler_reg, unsigned int flags)
 {
     struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
     uint32_t sampler_var_id, sampler_id, sampled_image_type_id;
@@ -9897,11 +9903,8 @@ static void spirv_compiler_emit_sync(struct spirv_compiler *compiler,
         bool global_uav = flags & VKD3DSSF_GLOBAL_UAV;
 
         if (group_uav && global_uav)
-        {
-            WARN("Invalid UAV sync flag combination; assuming global.\n");
             spirv_compiler_warning(compiler, VKD3D_SHADER_WARNING_SPV_INVALID_UAV_FLAGS,
                     "The flags for a UAV sync instruction are contradictory; assuming global sync.");
-        }
         memory_scope = global_uav ? SpvScopeDevice : SpvScopeWorkgroup;
         memory_semantics |= SpvMemorySemanticsUniformMemoryMask | SpvMemorySemanticsImageMemoryMask;
         flags &= ~(VKD3DSSF_THREAD_GROUP_UAV | VKD3DSSF_GLOBAL_UAV);
@@ -10005,7 +10008,7 @@ static void spirv_compiler_emit_quad_read_lane_at(struct spirv_compiler *compile
     const struct vsir_dst_operand *dst = instruction->dst;
     uint32_t type_id, val_id, lane_id;
 
-    if (!register_is_constant_or_undef(&src[1].reg))
+    if (!vsir_operand_is_constant_or_undef(&src[1].reg))
     {
         spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_NOT_IMPLEMENTED,
                 "Non-constant quad read lane indices are not supported.");
@@ -10174,7 +10177,7 @@ static void spirv_compiler_emit_wave_read_lane_at(struct spirv_compiler *compile
     lane_id = spirv_compiler_emit_load_src(compiler, &src[1], VKD3DSP_WRITEMASK_0);
 
     /* TODO: detect values loaded from a const buffer? */
-    if (register_is_constant_or_undef(&src[1].reg))
+    if (vsir_operand_is_constant_or_undef(&src[1].reg))
     {
         /* Uniform lane_id only. */
         val_id = vkd3d_spirv_build_op_group_nonuniform_broadcast(builder, type_id, val_id, lane_id);
@@ -10217,11 +10220,9 @@ static void spirv_compiler_emit_main_prolog(struct spirv_compiler *compiler)
         vkd3d_spirv_build_op(&compiler->spirv_builder.function_stream, SpvOpBeginInvocationInterlockEXT);
 }
 
-static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler,
+static void spirv_compiler_handle_instruction(struct spirv_compiler *compiler,
         const struct vkd3d_shader_instruction *instruction)
 {
-    int ret = VKD3D_OK;
-
     compiler->location = instruction->location;
     /* radeonsi from Mesa 20.3.5 seems to get confused by OpLine instructions
      * before OpFunction, seemingly causing it to fail to find the entry
@@ -10313,7 +10314,7 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler,
         case VSIR_OP_UTOF:
         case VSIR_OP_UTOU:
         case VSIR_OP_XOR:
-            ret = spirv_compiler_emit_alu_instruction(compiler, instruction);
+            spirv_compiler_emit_alu_instruction(compiler, instruction);
             break;
         case VSIR_OP_ISFINITE:
             spirv_compiler_emit_isfinite(compiler, instruction);
@@ -10590,8 +10591,6 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler,
                     vsir_opcode_get_name(instruction->opcode, "<unknown>"), instruction->opcode);
             break;
     }
-
-    return ret;
 }
 
 static void spirv_compiler_emit_io_declarations(struct spirv_compiler *compiler)
@@ -10653,7 +10652,7 @@ static void spirv_compiler_emit_descriptor_declarations(struct spirv_compiler *c
     for (i = 0; i < descriptors->descriptor_count; ++i)
     {
         const struct vkd3d_shader_descriptor_info1 *descriptor = &descriptors->descriptors[i];
-        struct vkd3d_shader_register_range range;
+        struct vsir_register_range range;
 
         range.first = descriptor->register_index;
         if (descriptor->count == ~0u)
@@ -10689,8 +10688,8 @@ static void spirv_compiler_emit_immediate_constant_buffers(struct spirv_compiler
     const struct vkd3d_shader_immediate_constant_buffer *icb;
     const struct vsir_program *program = compiler->program;
     uint32_t type_id, const_id, ptr_type_id, icb_id;
-    struct vkd3d_shader_register reg;
     struct vkd3d_symbol reg_symbol;
+    struct vsir_operand reg;
     size_t i;
 
     for (i = 0; i < program->icb_count; ++i)
@@ -10704,7 +10703,7 @@ static void spirv_compiler_emit_immediate_constant_buffers(struct spirv_compiler
         vkd3d_spirv_build_op_name(builder, icb_id, "icb%zu", icb->register_idx);
 
         /* Set an index count of 2 so vkd3d_symbol_make_register() uses idx[0] as a buffer id. */
-        vsir_register_init(&reg, VKD3DSPR_IMMCONSTBUFFER, VSIR_DATA_F32, 2);
+        vsir_operand_init(&reg, VKD3DSPR_IMMCONSTBUFFER, VSIR_DATA_F32, 2);
         reg.idx[0].offset = icb->register_idx;
         vkd3d_symbol_make_register(&reg_symbol, &reg);
         vkd3d_symbol_set_register_info(&reg_symbol, icb_id, SpvStorageClassPrivate,
@@ -10722,7 +10721,6 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler,
     struct vsir_program *program = compiler->program;
     enum vkd3d_shader_spirv_environment environment;
     struct vkd3d_shader_instruction *ins;
-    enum vkd3d_result result = VKD3D_OK;
     unsigned int i, max_element_count;
     struct vsir_program_iterator it;
 
@@ -10791,14 +10789,11 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler,
         spirv_compiler_emit_tessellator_domain(compiler, program->tess_domain);
 
     it = vsir_program_iterator(&program->instructions);
-    for (ins = vsir_program_iterator_head(&it); ins && result >= 0; ins = vsir_program_iterator_next(&it))
+    for (ins = vsir_program_iterator_head(&it); ins; ins = vsir_program_iterator_next(&it))
     {
-        result = spirv_compiler_handle_instruction(compiler, ins);
+        spirv_compiler_handle_instruction(compiler, ins);
     }
 
-    if (result < 0)
-        return result;
-
     if (!is_in_default_phase(compiler))
         spirv_compiler_leave_shader_phase(compiler);
     else
@@ -10848,7 +10843,7 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler,
         if (!vkd3d_spirv_validate(&buffer, spirv, environment))
         {
             FIXME("Failed to validate SPIR-V binary.\n");
-            vkd3d_shader_trace_text(buffer.buffer, buffer.content_size);
+            vkd3d_string_buffer_trace(&buffer);
 
             if (compiler->config_flags & VKD3D_SHADER_CONFIG_FLAG_FORCE_VALIDATION)
             {
diff --git a/libs/vkd3d/libs/vkd3d-shader/tpf.c b/libs/vkd3d/libs/vkd3d-shader/tpf.c
index 003a6635aaf4..c50f6ebcbf54 100644
--- a/libs/vkd3d/libs/vkd3d-shader/tpf.c
+++ b/libs/vkd3d/libs/vkd3d-shader/tpf.c
@@ -84,7 +84,7 @@ STATIC_ASSERT(SM4_MAX_SRC_COUNT <= SPIRV_MAX_SRC_COUNT);
 #define VKD3D_SM4_INTERPOLATION_MODE_MASK     (0xfu << VKD3D_SM4_INTERPOLATION_MODE_SHIFT)
 
 #define VKD3D_SM4_GLOBAL_FLAGS_SHIFT          11
-#define VKD3D_SM4_GLOBAL_FLAGS_MASK           (0xffu << VKD3D_SM4_GLOBAL_FLAGS_SHIFT)
+#define VKD3D_SM4_GLOBAL_FLAGS_MASK           (0x1ffu << VKD3D_SM4_GLOBAL_FLAGS_SHIFT)
 
 #define VKD3D_SM5_PRECISE_SHIFT               19
 #define VKD3D_SM5_PRECISE_MASK                (0xfu << VKD3D_SM5_PRECISE_SHIFT)
@@ -837,7 +837,6 @@ static void shader_sm4_read_shader_data(struct vkd3d_shader_instruction *ins, ui
 
     if (!(icb = vkd3d_malloc(offsetof(struct vkd3d_shader_immediate_constant_buffer, data[icb_size]))))
     {
-        ERR("Failed to allocate immediate constant buffer, size %u.\n", icb_size);
         vkd3d_shader_parser_error(&priv->p, VKD3D_SHADER_ERROR_TPF_OUT_OF_MEMORY, "Out of memory.");
         ins->opcode = VSIR_OP_INVALID;
         return;
@@ -853,16 +852,13 @@ static void shader_sm4_read_shader_data(struct vkd3d_shader_instruction *ins, ui
 }
 
 static void shader_sm4_set_descriptor_register_range(struct vkd3d_shader_sm4_parser *sm4,
-        const struct vkd3d_shader_register *reg, struct vkd3d_shader_register_range *range)
+        const struct vsir_operand *reg, struct vsir_register_range *range)
 {
     range->first = reg->idx[1].offset;
     range->last = reg->idx[shader_is_sm_5_1(sm4) ? 2 : 1].offset;
     if (range->last < range->first)
-    {
-        FIXME("Invalid register range [%u:%u].\n", range->first, range->last);
         vkd3d_shader_parser_error(&sm4->p, VKD3D_SHADER_ERROR_TPF_INVALID_REGISTER_RANGE,
                 "Last register %u must not be less than first register %u in range.", range->last, range->first);
-    }
 }
 
 static void shader_sm4_read_dcl_resource(struct vkd3d_shader_instruction *ins, uint32_t opcode,
@@ -1015,7 +1011,6 @@ static void shader_sm4_read_dcl_index_range(struct vkd3d_shader_instruction *ins
             break;
 
         default:
-            WARN("Unhandled register type %#x.\n", type);
             vkd3d_shader_parser_error(&priv->p, VKD3D_SHADER_ERROR_TPF_INVALID_INDEX_RANGE_DCL,
                     "Invalid register type %#x for index range base %u, count %u, mask %#x.",
                     type, register_idx, register_count, write_mask);
@@ -1035,8 +1030,6 @@ static void shader_sm4_read_dcl_index_range(struct vkd3d_shader_instruction *ins
         if ((r.index <= register_idx && register_idx - r.index < r.count)
                 || (register_idx < r.index && r.index - register_idx < register_count))
         {
-            WARN("Detected index range collision for base %u, count %u, mask %#x.\n",
-                    register_idx, register_count, write_mask);
             vkd3d_shader_parser_error(&priv->p, VKD3D_SHADER_ERROR_TPF_INVALID_INDEX_RANGE_DCL,
                     "Register index range base %u, count %u, mask %#x collides with a previous declaration.",
                     register_idx, register_count, write_mask);
@@ -1055,7 +1048,6 @@ static void shader_sm4_read_dcl_index_range(struct vkd3d_shader_instruction *ins
         if (e && e->sysval_semantic && register_count > 1 && !vsir_sysval_semantic_is_tess_factor(e->sysval_semantic)
                 && !vsir_sysval_semantic_is_clip_cull(e->sysval_semantic))
         {
-            WARN("Sysval %u included in an index range declaration.\n", e->sysval_semantic);
             vkd3d_shader_parser_error(&priv->p, VKD3D_SHADER_ERROR_TPF_INVALID_INDEX_RANGE_DCL,
                     "Index range base %u, count %u, mask %#x contains sysval %u.",
                     register_idx, register_count, write_mask, e->sysval_semantic);
@@ -1064,8 +1056,6 @@ static void shader_sm4_read_dcl_index_range(struct vkd3d_shader_instruction *ins
         if ((io_masks[register_idx + i] & write_mask) != write_mask
                 && (io_masks[register_idx + i] & write_mask) != 0)
         {
-            WARN("No matching declaration for index range base %u, count %u, mask %#x.\n",
-                    register_idx, register_count, write_mask);
             vkd3d_shader_parser_error(&priv->p, VKD3D_SHADER_ERROR_TPF_INVALID_INDEX_RANGE_DCL,
                     "Input/output registers matching index range base %u, count %u, mask %#x were not declared.",
                     register_idx, register_count, write_mask);
@@ -1204,7 +1194,7 @@ static void shader_sm4_read_dcl_global_flags(struct vkd3d_shader_instruction *in
 
     if (sm4->program->global_flags & (VKD3DSGF_ENABLE_DOUBLE_PRECISION_FLOAT_OPS
             | VKD3DSGF_ENABLE_11_1_DOUBLE_EXTENSIONS))
-        sm4->program->f64_denorm_mode = VSIR_DENORM_PRESERVE;
+        sm4->program->f64_denormal_mode = VKD3D_SHADER_DENORMAL_MODE_PRESERVE;
 }
 
 static void shader_sm5_read_fcall(struct vkd3d_shader_instruction *ins, uint32_t opcode, uint32_t opcode_token,
@@ -1410,13 +1400,15 @@ struct tpf_compiler
     struct dxbc_writer dxbc;
 };
 
-static void VKD3D_PRINTF_FUNC(3, 4) tpf_compiler_error(struct tpf_compiler *tpf,
-        enum vkd3d_shader_error error, const char *fmt, ...)
+#define tpf_compiler_error(tpf, error, ...) \
+        tpf_compiler_error_(tpf, error, __FUNCTION__, __VA_ARGS__)
+static void VKD3D_PRINTF_FUNC(4, 5) tpf_compiler_error_(struct tpf_compiler *tpf,
+        enum vkd3d_shader_error error, const char *function, const char *fmt, ...)
 {
     va_list args;
 
     va_start(args, fmt);
-    vkd3d_shader_verror(tpf->message_context, &tpf->location, error, fmt, args);
+    vkd3d_shader_verror(tpf->message_context, &tpf->location, error, function, fmt, args);
     va_end(args);
 
     if (!tpf->first_error)
@@ -2004,7 +1996,7 @@ static enum vsir_data_type map_data_type(char t)
 }
 
 static bool shader_sm4_read_reg_idx(struct vkd3d_shader_sm4_parser *tpf, const uint32_t **ptr,
-        const uint32_t *end, uint32_t addressing, struct vkd3d_shader_register_index *reg_idx)
+        const uint32_t *end, uint32_t addressing, struct vsir_register_index *reg_idx)
 {
     if (addressing & VKD3D_SM4_ADDRESSING_RELATIVE)
     {
@@ -2032,7 +2024,7 @@ static bool shader_sm4_read_reg_idx(struct vkd3d_shader_sm4_parser *tpf, const u
 }
 
 static bool shader_sm4_read_param(struct vkd3d_shader_sm4_parser *priv, const uint32_t **ptr, const uint32_t *end,
-        enum vsir_data_type data_type, struct vkd3d_shader_register *param, enum vkd3d_shader_src_modifier *modifier)
+        enum vsir_data_type data_type, struct vsir_operand *param, enum vkd3d_shader_src_modifier *modifier)
 {
     const struct vkd3d_sm4_register_type_info *register_type_info;
     enum vkd3d_shader_register_type vsir_register_type;
@@ -2064,7 +2056,7 @@ static bool shader_sm4_read_param(struct vkd3d_shader_sm4_parser *priv, const ui
 
     order = (token & VKD3D_SM4_REGISTER_ORDER_MASK) >> VKD3D_SM4_REGISTER_ORDER_SHIFT;
 
-    vsir_register_init(param, vsir_register_type, data_type, order);
+    vsir_operand_init(param, vsir_register_type, data_type, order);
     param->precision = VKD3D_SHADER_REGISTER_PRECISION_DEFAULT;
     param->non_uniform = false;
 
@@ -2211,13 +2203,10 @@ static bool shader_sm4_read_param(struct vkd3d_shader_sm4_parser *priv, const ui
     else if (register_type == VKD3D_SM4_RT_IMMCONSTBUFFER)
     {
         if (param->idx_count != 1)
-        {
-            WARN("Unexpected idx count %u.\n", param->idx_count);
             vkd3d_shader_parser_error(&priv->p, VKD3D_SHADER_ERROR_TPF_INVALID_REGISTER_INDEX_COUNT,
                     "Invalid index count %u for immediate const buffer register; expected count 1.", param->idx_count);
-        }
     }
-    else if (!shader_is_sm_5_1(priv) && vsir_register_is_descriptor(param))
+    else if (!shader_is_sm_5_1(priv) && vsir_operand_is_descriptor(param))
     {
         /* SM5.1 places a symbol identifier in idx[0] and moves
          * other values up one slot. Normalize to SM5.1. */
@@ -2229,7 +2218,7 @@ static bool shader_sm4_read_param(struct vkd3d_shader_sm4_parser *priv, const ui
     return true;
 }
 
-bool shader_sm4_is_scalar_register(const struct vkd3d_shader_register *reg)
+bool shader_sm4_is_scalar_register(const struct vsir_operand *reg)
 {
     switch (reg->type)
     {
@@ -2261,7 +2250,7 @@ static uint32_t swizzle_to_sm4(uint32_t s)
     return ret;
 }
 
-static bool register_is_input_output(const struct vkd3d_shader_register *reg)
+static bool register_is_input_output(const struct vsir_operand *reg)
 {
     switch (reg->type)
     {
@@ -2278,7 +2267,7 @@ static bool register_is_input_output(const struct vkd3d_shader_register *reg)
     }
 }
 
-static bool register_is_control_point_input(const struct vkd3d_shader_register *reg,
+static bool register_is_control_point_input(const struct vsir_operand *reg,
         const struct vkd3d_shader_sm4_parser *priv)
 {
     return reg->type == VKD3DSPR_INCONTROLPOINT || reg->type == VKD3DSPR_OUTCONTROLPOINT
@@ -2295,7 +2284,7 @@ static uint32_t mask_from_swizzle(uint32_t swizzle)
 }
 
 static bool shader_sm4_validate_input_output_register(struct vkd3d_shader_sm4_parser *priv,
-        const struct vkd3d_shader_register *reg, unsigned int mask)
+        const struct vsir_operand *reg, unsigned int mask)
 {
     unsigned int idx_count = 1 + register_is_control_point_input(reg, priv);
     const unsigned int *masks;
@@ -2335,8 +2324,6 @@ static bool shader_sm4_validate_input_output_register(struct vkd3d_shader_sm4_pa
     /* The signature element registers have already been checked against MAX_REG_OUTPUT. */
     if (register_idx >= MAX_REG_OUTPUT || (masks[register_idx] & mask) != mask)
     {
-        WARN("Failed to find signature element for register type %#x, index %u and mask %#x.\n",
-                reg->type, register_idx, mask);
         vkd3d_shader_parser_error(&priv->p, VKD3D_SHADER_ERROR_TPF_INVALID_IO_REGISTER,
                 "Could not find signature element matching register type %#x, index %u and mask %#x.",
                 reg->type, register_idx, mask);
@@ -2384,7 +2371,7 @@ static bool tpf_read_src_operand(struct vkd3d_shader_sm4_parser *tpf, const uint
 
                     mask = (token & VKD3D_SM4_WRITEMASK_MASK) >> VKD3D_SM4_WRITEMASK_SHIFT;
                     /* Mask seems only to be used for vec4 constants and is always zero. */
-                    if (!register_is_constant(&src->reg))
+                    if (!vsir_operand_is_constant(&src->reg))
                         vkd3d_shader_parser_warning(&tpf->p, VKD3D_SHADER_WARNING_TPF_UNHANDLED_REGISTER_MASK,
                                 "Unhandled mask %#x for a non-constant source register.", mask);
                     else if (mask)
@@ -2799,7 +2786,7 @@ static bool shader_sm4_init(struct vkd3d_shader_sm4_parser *sm4, struct vsir_pro
             &version, token_count / 7u + 20, VSIR_CF_STRUCTURED, VSIR_NORMALISED_SM4))
         return false;
 
-    program->f32_denorm_mode = VSIR_DENORM_FLUSH_TO_ZERO;
+    program->f32_denormal_mode = VKD3D_SHADER_DENORMAL_MODE_FLUSH_TO_ZERO;
 
     vkd3d_shader_parser_init(&sm4->p, message_context, compile_info->source_name);
     sm4->ptr = sm4->start;
@@ -2831,8 +2818,6 @@ static bool shader_sm4_parser_validate_signature(struct vkd3d_shader_sm4_parser
         register_count = signature->elements[i].register_count;
         if (register_idx != ~0u && (register_idx >= MAX_REG_OUTPUT || MAX_REG_OUTPUT - register_idx < register_count))
         {
-            WARN("%s signature element %u unhandled register index %u, count %u.\n",
-                    name, i, register_idx, register_count);
             vkd3d_shader_parser_error(&sm4->p, VKD3D_SHADER_ERROR_TPF_TOO_MANY_REGISTERS,
                     "%s signature element %u register index %u, count %u exceeds maximum index of %u.", name,
                     i, register_idx, register_count, MAX_REG_OUTPUT - 1);
@@ -2840,11 +2825,8 @@ static bool shader_sm4_parser_validate_signature(struct vkd3d_shader_sm4_parser
         }
 
         if (!vkd3d_bitmask_is_contiguous(mask = signature->elements[i].mask))
-        {
-            WARN("%s signature element %u mask %#x is not contiguous.\n", name, i, mask);
             vkd3d_shader_parser_warning(&sm4->p, VKD3D_SHADER_WARNING_TPF_MASK_NOT_CONTIGUOUS,
                     "%s signature element %u mask %#x is not contiguous.", name, i, mask);
-        }
 
         if (register_idx != ~0u)
             masks[register_idx] |= mask;
@@ -2875,7 +2857,6 @@ static void shader_sm4_validate_default_phase_index_ranges(struct vkd3d_shader_s
     }
 
     /* This is very unlikely to occur and would complicate the default control point phase implementation. */
-    WARN("Default phase index ranges are not identical.\n");
     vkd3d_shader_parser_error(&sm4->p, VKD3D_SHADER_ERROR_TPF_INVALID_INDEX_RANGE_DCL,
             "Default control point phase input and output index range declarations are not identical.");
     return;
@@ -3010,6 +2991,7 @@ bool sm4_register_from_semantic_name(const struct vkd3d_shader_version *version,
         {"sv_depth",            true,  VKD3D_SHADER_TYPE_PIXEL,    VKD3DSPR_DEPTHOUT,      false},
         {"sv_target",           true,  VKD3D_SHADER_TYPE_PIXEL,    VKD3DSPR_OUTPUT,        true},
         {"sv_coverage",         true,  VKD3D_SHADER_TYPE_PIXEL,    VKD3DSPR_SAMPLEMASK,    false},
+        {"sv_stencilref",       true,  VKD3D_SHADER_TYPE_PIXEL,    VKD3DSPR_OUTSTENCILREF, false},
     };
 
     for (i = 0; i < ARRAY_SIZE(register_table); ++i)
@@ -3077,10 +3059,12 @@ static bool get_insidetessfactor_sysval_semantic(enum vkd3d_shader_sysval_semant
 }
 
 bool sm4_sysval_semantic_from_semantic_name(enum vkd3d_shader_sysval_semantic *sysval_semantic,
-        const struct vkd3d_shader_version *version, bool semantic_compat_mapping, enum vkd3d_tessellator_domain domain,
-        const char *semantic_name, unsigned int semantic_idx, bool output,
-        bool is_patch_constant_func, bool is_primitive)
+        const struct vkd3d_shader_version *version,
+        enum vkd3d_shader_compile_option_backward_compatibility compatibility_flags,
+        enum vkd3d_tessellator_domain domain, const char *semantic_name,
+        unsigned int semantic_idx, bool output, bool is_patch_constant_func, bool is_primitive)
 {
+    bool semantic_compat_mapping = (compatibility_flags & VKD3D_SHADER_COMPILE_OPTION_BACKCOMPAT_MAP_SEMANTIC_NAMES);
     unsigned int i;
 
     static const struct
@@ -3097,53 +3081,72 @@ bool sm4_sysval_semantic_from_semantic_name(enum vkd3d_shader_sysval_semantic *s
         {"sv_groupindex",               false, VKD3D_SHADER_TYPE_COMPUTE,   ~0u},
         {"sv_groupthreadid",            false, VKD3D_SHADER_TYPE_COMPUTE,   ~0u},
 
+        {"sv_clipdistance",             false, VKD3D_SHADER_TYPE_DOMAIN,    VKD3D_SHADER_SV_NONE},
+        {"sv_culldistance",             false, VKD3D_SHADER_TYPE_DOMAIN,    VKD3D_SHADER_SV_NONE},
         {"sv_domainlocation",           false, VKD3D_SHADER_TYPE_DOMAIN,    ~0u},
         {"sv_position",                 false, VKD3D_SHADER_TYPE_DOMAIN,    VKD3D_SHADER_SV_NONE},
         {"sv_primitiveid",              false, VKD3D_SHADER_TYPE_DOMAIN,    ~0u},
         {"sv_rendertargetarrayindex",   false, VKD3D_SHADER_TYPE_DOMAIN,    VKD3D_SHADER_SV_NONE},
         {"sv_viewportarrayindex",       false, VKD3D_SHADER_TYPE_DOMAIN,    VKD3D_SHADER_SV_NONE},
 
+        {"sv_clipdistance",             true,  VKD3D_SHADER_TYPE_DOMAIN,    VKD3D_SHADER_SV_CLIP_DISTANCE},
+        {"sv_culldistance",             true,  VKD3D_SHADER_TYPE_DOMAIN,    VKD3D_SHADER_SV_CULL_DISTANCE},
         {"sv_position",                 true,  VKD3D_SHADER_TYPE_DOMAIN,    VKD3D_SHADER_SV_POSITION},
         {"sv_rendertargetarrayindex",   true,  VKD3D_SHADER_TYPE_DOMAIN,    VKD3D_SHADER_SV_RENDER_TARGET_ARRAY_INDEX},
         {"sv_viewportarrayindex",       true,  VKD3D_SHADER_TYPE_DOMAIN,    VKD3D_SHADER_SV_VIEWPORT_ARRAY_INDEX},
 
-        {"sv_primitiveid",              false, VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_PRIMITIVE_ID},
+        {"sv_clipdistance",             false, VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_CLIP_DISTANCE},
+        {"sv_culldistance",             false, VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_CULL_DISTANCE},
         {"sv_gsinstanceid",             false, VKD3D_SHADER_TYPE_GEOMETRY,  ~0u},
+        {"sv_primitiveid",              false, VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_PRIMITIVE_ID},
 
         {"position",                    true,  VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_POSITION},
+        {"sv_clipdistance",             true,  VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_CLIP_DISTANCE},
+        {"sv_culldistance",             true,  VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_CULL_DISTANCE},
+        {"sv_isfrontface",              true,  VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_IS_FRONT_FACE},
         {"sv_position",                 true,  VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_POSITION},
         {"sv_primitiveid",              true,  VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_PRIMITIVE_ID},
-        {"sv_isfrontface",              true,  VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_IS_FRONT_FACE},
         {"sv_rendertargetarrayindex",   true,  VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_RENDER_TARGET_ARRAY_INDEX},
         {"sv_viewportarrayindex",       true,  VKD3D_SHADER_TYPE_GEOMETRY,  VKD3D_SHADER_SV_VIEWPORT_ARRAY_INDEX},
 
+        {"sv_clipdistance",             false, VKD3D_SHADER_TYPE_HULL,      VKD3D_SHADER_SV_CLIP_DISTANCE},
+        {"sv_culldistance",             false, VKD3D_SHADER_TYPE_HULL,      VKD3D_SHADER_SV_CULL_DISTANCE},
         {"sv_outputcontrolpointid",     false, VKD3D_SHADER_TYPE_HULL,      ~0u},
         {"sv_position",                 false, VKD3D_SHADER_TYPE_HULL,      ~0u},
         {"sv_primitiveid",              false, VKD3D_SHADER_TYPE_HULL,      ~0u},
 
+        {"sv_clipdistance",             true,  VKD3D_SHADER_TYPE_HULL,      VKD3D_SHADER_SV_CLIP_DISTANCE},
+        {"sv_culldistance",             true,  VKD3D_SHADER_TYPE_HULL,      VKD3D_SHADER_SV_CULL_DISTANCE},
         {"sv_position",                 true,  VKD3D_SHADER_TYPE_HULL,      VKD3D_SHADER_SV_POSITION},
         {"sv_rendertargetarrayindex",   true,  VKD3D_SHADER_TYPE_HULL,      VKD3D_SHADER_SV_RENDER_TARGET_ARRAY_INDEX},
         {"sv_viewportarrayindex",       true,  VKD3D_SHADER_TYPE_HULL,      VKD3D_SHADER_SV_VIEWPORT_ARRAY_INDEX},
 
         {"position",                    false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_POSITION},
+        {"sv_clipdistance",             false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_CLIP_DISTANCE},
+        {"sv_culldistance",             false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_CULL_DISTANCE},
+        {"sv_isfrontface",              false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_IS_FRONT_FACE},
         {"sv_position",                 false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_POSITION},
         {"sv_primitiveid",              false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_PRIMITIVE_ID},
-        {"sv_isfrontface",              false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_IS_FRONT_FACE},
         {"sv_rendertargetarrayindex",   false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_RENDER_TARGET_ARRAY_INDEX},
-        {"sv_viewportarrayindex",       false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_VIEWPORT_ARRAY_INDEX},
         {"sv_sampleindex",              false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_SAMPLE_INDEX},
+        {"sv_viewportarrayindex",       false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_VIEWPORT_ARRAY_INDEX},
+        {"vface",                       false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_IS_FRONT_FACE},
+        {"vpos",                        false, VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_POSITION},
 
         {"color",                       true,  VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_TARGET},
         {"depth",                       true,  VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_DEPTH},
-        {"sv_target",                   true,  VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_TARGET},
-        {"sv_depth",                    true,  VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_DEPTH},
         {"sv_coverage",                 true,  VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_COVERAGE},
+        {"sv_depth",                    true,  VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_DEPTH},
+        {"sv_stencilref",               true,  VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_STENCIL_REF},
+        {"sv_target",                   true,  VKD3D_SHADER_TYPE_PIXEL,     VKD3D_SHADER_SV_TARGET},
 
+        {"sv_instanceid",               false, VKD3D_SHADER_TYPE_VERTEX,    VKD3D_SHADER_SV_INSTANCE_ID},
         {"sv_position",                 false, VKD3D_SHADER_TYPE_VERTEX,    VKD3D_SHADER_SV_NONE},
         {"sv_vertexid",                 false, VKD3D_SHADER_TYPE_VERTEX,    VKD3D_SHADER_SV_VERTEX_ID},
-        {"sv_instanceid",               false, VKD3D_SHADER_TYPE_VERTEX,    VKD3D_SHADER_SV_INSTANCE_ID},
 
         {"position",                    true,  VKD3D_SHADER_TYPE_VERTEX,    VKD3D_SHADER_SV_POSITION},
+        {"sv_clipdistance",             true,  VKD3D_SHADER_TYPE_VERTEX,    VKD3D_SHADER_SV_CLIP_DISTANCE},
+        {"sv_culldistance",             true,  VKD3D_SHADER_TYPE_VERTEX,    VKD3D_SHADER_SV_CULL_DISTANCE},
         {"sv_position",                 true,  VKD3D_SHADER_TYPE_VERTEX,    VKD3D_SHADER_SV_POSITION},
         {"sv_rendertargetarrayindex",   true,  VKD3D_SHADER_TYPE_VERTEX,    VKD3D_SHADER_SV_RENDER_TARGET_ARRAY_INDEX},
         {"sv_viewportarrayindex",       true,  VKD3D_SHADER_TYPE_VERTEX,    VKD3D_SHADER_SV_VIEWPORT_ARRAY_INDEX},
@@ -3161,6 +3164,10 @@ bool sm4_sysval_semantic_from_semantic_name(enum vkd3d_shader_sysval_semantic *s
             *sysval_semantic = VKD3D_SHADER_SV_RENDER_TARGET_ARRAY_INDEX;
         else if (!ascii_strcasecmp(semantic_name, "sv_viewportarrayindex"))
             *sysval_semantic = VKD3D_SHADER_SV_VIEWPORT_ARRAY_INDEX;
+        else if (!ascii_strcasecmp(semantic_name, "sv_clipdistance") && semantic_idx <= 1)
+            *sysval_semantic = VKD3D_SHADER_SV_CLIP_DISTANCE;
+        else if (!ascii_strcasecmp(semantic_name, "sv_culldistance") && semantic_idx <= 1)
+            *sysval_semantic = VKD3D_SHADER_SV_CULL_DISTANCE;
         else if (has_sv_prefix)
             return false;
         else
@@ -3206,6 +3213,8 @@ bool sm4_sysval_semantic_from_semantic_name(enum vkd3d_shader_sysval_semantic *s
                 && (semantic_compat_mapping || has_sv_prefix)
                 && version->type == semantics[i].shader_type)
         {
+            if (vsir_sysval_semantic_is_clip_cull(semantics[i].semantic) && semantic_idx > 1)
+                return false;
             if (is_patch_constant_func && output && semantics[i].semantic != ~0u)
                 *sysval_semantic = VKD3D_SHADER_SV_NONE;
             else
@@ -3417,8 +3426,7 @@ struct sm4_instruction
     unsigned int idx_src_count;
 };
 
-static unsigned int sm4_get_index_addressing_from_reg(const struct vkd3d_shader_register *reg,
-        unsigned int i)
+static unsigned int sm4_get_index_addressing_from_reg(const struct vsir_operand *reg, unsigned int i)
 {
     if (reg->idx[i].rel_addr)
     {
@@ -3431,7 +3439,7 @@ static unsigned int sm4_get_index_addressing_from_reg(const struct vkd3d_shader_
     return 0;
 }
 
-static uint32_t sm4_encode_register(const struct tpf_compiler *tpf, const struct vkd3d_shader_register *reg,
+static uint32_t sm4_encode_register(const struct tpf_compiler *tpf, const struct vsir_operand *reg,
         enum vkd3d_sm4_swizzle_type sm4_swizzle_type, uint32_t sm4_swizzle)
 {
     const struct vkd3d_sm4_register_type_info *register_type_info;
@@ -3471,7 +3479,7 @@ static uint32_t sm4_encode_register(const struct tpf_compiler *tpf, const struct
         switch (sm4_swizzle_type)
         {
             case VKD3D_SM4_SWIZZLE_NONE:
-                if (register_is_constant(reg))
+                if (vsir_operand_is_constant(reg))
                     break;
                 VKD3D_ASSERT(sm4_swizzle);
                 token |= (sm4_swizzle << VKD3D_SM4_WRITEMASK_SHIFT) & VKD3D_SM4_WRITEMASK_MASK;
@@ -3493,11 +3501,10 @@ static uint32_t sm4_encode_register(const struct tpf_compiler *tpf, const struct
     return token;
 }
 
-static void sm4_write_register_index(const struct tpf_compiler *tpf, const struct vkd3d_shader_register *reg,
-        unsigned int j)
+static void sm4_write_register_index(const struct tpf_compiler *tpf, const struct vsir_operand *reg, unsigned int j)
 {
     unsigned int addressing = sm4_get_index_addressing_from_reg(reg, j);
-    const struct vkd3d_shader_register_index *idx = &reg->idx[j];
+    const struct vsir_register_index *idx = &reg->idx[j];
     struct vkd3d_bytecode_buffer *buffer = tpf->buffer;
     unsigned int k;
 
@@ -3944,7 +3951,7 @@ static void write_sm4_dcl_global_flags(struct tpf_compiler *tpf, uint32_t flags)
     struct sm4_instruction instr =
     {
         .opcode = VKD3D_SM4_OP_DCL_GLOBAL_FLAGS,
-        .extra_bits = flags << VKD3D_SM4_GLOBAL_FLAGS_SHIFT,
+        .extra_bits = (flags << VKD3D_SM4_GLOBAL_FLAGS_SHIFT) & VKD3D_SM4_GLOBAL_FLAGS_MASK,
     };
 
     write_sm4_instruction(tpf, &instr);
@@ -4070,7 +4077,7 @@ static void tpf_write_dcl_vertices_out(struct tpf_compiler *tpf, unsigned int co
 
 /* Descriptor registers are stored in shader model 5.1 format regardless
  * of the program's version. Convert them to the 4.0 format if necessary. */
-static void rewrite_descriptor_register(const struct tpf_compiler *tpf, struct vkd3d_shader_register *reg)
+static void rewrite_descriptor_register(const struct tpf_compiler *tpf, struct vsir_operand *reg)
 {
     if (vkd3d_shader_ver_ge(&tpf->program->shader_version, 5, 1))
         return;
@@ -4428,7 +4435,7 @@ static void tpf_write_shdr(struct tpf_compiler *tpf)
     put_u32(&buffer, vkd3d_make_u32((version->major << 4) | version->minor, shader_types[version->type]));
     token_count_position = put_u32(&buffer, 0);
 
-    if (program->global_flags)
+    if (program->global_flags & (VKD3D_SM4_GLOBAL_FLAGS_MASK >> VKD3D_SM4_GLOBAL_FLAGS_SHIFT))
         write_sm4_dcl_global_flags(tpf, program->global_flags);
 
     if (version->type == VKD3D_SHADER_TYPE_HULL)
@@ -4473,6 +4480,8 @@ static void tpf_write_sfi0(struct tpf_compiler *tpf)
 
     if (tpf->program->global_flags & VKD3DSGF_ENABLE_MINIMUM_PRECISION)
         *flags |= DXBC_SFI0_REQUIRES_MINIMUM_PRECISION;
+    if (tpf->program->global_flags & VKD3DSGF_ENABLE_STENCIL_REF)
+        *flags |= DXBC_SFI0_REQUIRES_STENCIL_REF;
 
     /* FIXME: We also emit code that should require UAVS_AT_EVERY_STAGE,
      * STENCIL_REF, and TYPED_UAV_LOAD_ADDITIONAL_FORMATS. */
diff --git a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_main.c
index ddbf91ac482c..bf4ab9a88f78 100644
--- a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_main.c
+++ b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_main.c
@@ -23,6 +23,20 @@
 #include <stdio.h>
 #include <math.h>
 
+float vkd3d_parse_float(const char *s, vkd3d_locale l)
+{
+#ifdef HAVE_STRTOF_L
+    return strtof_l(s, NULL, l);
+#elif HAVE__STRTOF_L
+    return _strtof_l(s, NULL, l);
+#elif HAVE__STRTOD_L
+    return _strtod_l(s, NULL, l);
+#else
+#warning "Neither strtof_l() no strtod_l() is available, using strtof()."
+    return strtof(s, NULL);
+#endif
+}
+
 static inline int char_to_int(char c)
 {
     if ('0' <= c && c <= '9')
@@ -164,6 +178,27 @@ int vkd3d_string_buffer_printf(struct vkd3d_string_buffer *buffer, const char *f
     return ret;
 }
 
+int vkd3d_string_buffer_print_f16(struct vkd3d_string_buffer *buffer, uint16_t f)
+{
+    size_t idx = buffer->content_size + 1;
+    union
+    {
+        uint32_t u32;
+        float f32;
+    } v;
+    int ret;
+
+    v.u32 = vkd3d_f32_from_f16(f);
+    if (!(ret = vkd3d_string_buffer_printf(buffer, "%.4e", v.f32)) && isfinite(v.f32))
+    {
+        if (signbit(v.f32))
+            ++idx;
+        buffer->buffer[idx] = '.';
+    }
+
+    return ret;
+}
+
 int vkd3d_string_buffer_print_f32(struct vkd3d_string_buffer *buffer, float f)
 {
     size_t idx = buffer->content_size + 1;
@@ -250,24 +285,8 @@ fail:
 
 void vkd3d_string_buffer_trace_(const struct vkd3d_string_buffer *buffer, const char *function)
 {
-    vkd3d_shader_trace_text_(buffer->buffer, buffer->content_size, function);
-}
-
-void vkd3d_shader_trace_text_(const char *text, size_t size, const char *function)
-{
-    const char *p, *q, *end = text + size;
-
-    if (!TRACE_ON())
-        return;
-
-    for (p = text; p < end; p = q)
-    {
-        if (!(q = memchr(p, '\n', end - p)))
-            q = end;
-        else
-            ++q;
-        vkd3d_dbg_printf(VKD3D_DEBUG_ENV_NAME, VKD3D_DBG_LEVEL_TRACE, function, "%.*s", (int)(q - p), p);
-    }
+    vkd3d_debug_channel_print_text(vkd3d_debug_channel_default, VKD3D_DEBUG_ENV_NAME,
+            VKD3D_DEBUG_CLASS_TRACE, function, buffer->buffer, buffer->content_size);
 }
 
 void vkd3d_string_buffer_cache_init(struct vkd3d_string_buffer_cache *cache)
@@ -366,7 +385,7 @@ void vkd3d_shader_string_from_message_context(char **out, struct vkd3d_shader_me
 }
 
 void vkd3d_shader_vnote(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
-        enum vkd3d_shader_log_level level, const char *format, va_list args)
+        enum vkd3d_shader_log_level level, const char *function, const char *format, va_list args)
 {
     struct vkd3d_string_buffer *messages = &context->messages;
     size_t pos = messages->content_size;
@@ -387,13 +406,14 @@ void vkd3d_shader_vnote(struct vkd3d_shader_message_context *context, const stru
     vkd3d_string_buffer_vprintf(messages, format, args);
     vkd3d_string_buffer_printf(messages, "\n");
 
-    WARN("%.*s", (int)(messages->content_size - pos), &messages->buffer[pos]);
+    vkd3d_debug_channel_printf(vkd3d_debug_channel_default, VKD3D_DEBUG_ENV_NAME, VKD3D_DEBUG_CLASS_WARN,
+            function, "%.*s", (int)(messages->content_size - pos), &messages->buffer[pos]);
     if (context->log_level < level)
         messages->content_size = pos;
 }
 
 void vkd3d_shader_vwarning(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
-        enum vkd3d_shader_error error, const char *format, va_list args)
+        enum vkd3d_shader_error error, const char *function, const char *format, va_list args)
 {
     struct vkd3d_string_buffer *messages = &context->messages;
     size_t pos = messages->content_size;
@@ -418,23 +438,24 @@ void vkd3d_shader_vwarning(struct vkd3d_shader_message_context *context, const s
     vkd3d_string_buffer_vprintf(messages, format, args);
     vkd3d_string_buffer_printf(messages, "\n");
 
-    WARN("%.*s", (int)(messages->content_size - pos), &messages->buffer[pos]);
+    vkd3d_debug_channel_printf(vkd3d_debug_channel_default, VKD3D_DEBUG_ENV_NAME, VKD3D_DEBUG_CLASS_WARN,
+            function, "%.*s", (int)(messages->content_size - pos), &messages->buffer[pos]);
     if (context->log_level < VKD3D_SHADER_LOG_WARNING)
         messages->content_size = pos;
 }
 
-void vkd3d_shader_warning(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
-        enum vkd3d_shader_error error, const char *format, ...)
+void vkd3d_shader_warning_(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_vwarning(context, location, error, format, args);
+    vkd3d_shader_vwarning(context, location, error, function, format, args);
     va_end(args);
 }
 
 void vkd3d_shader_verror(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
-        enum vkd3d_shader_error error, const char *format, va_list args)
+        enum vkd3d_shader_error error, const char *function, const char *format, va_list args)
 {
     struct vkd3d_string_buffer *messages = &context->messages;
     size_t pos = messages->content_size;
@@ -459,18 +480,19 @@ void vkd3d_shader_verror(struct vkd3d_shader_message_context *context, const str
     vkd3d_string_buffer_vprintf(messages, format, args);
     vkd3d_string_buffer_printf(messages, "\n");
 
-    WARN("%.*s", (int)(messages->content_size - pos), &messages->buffer[pos]);
+    vkd3d_debug_channel_printf(vkd3d_debug_channel_default, VKD3D_DEBUG_ENV_NAME, VKD3D_DEBUG_CLASS_WARN,
+            function, "%.*s", (int)(messages->content_size - pos), &messages->buffer[pos]);
     if (context->log_level < VKD3D_SHADER_LOG_ERROR)
         messages->content_size = pos;
 }
 
-void vkd3d_shader_error(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
-        enum vkd3d_shader_error error, const char *format, ...)
+void vkd3d_shader_error_(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_verror(context, location, error, format, args);
+    vkd3d_shader_verror(context, location, error, function, format, args);
     va_end(args);
 }
 
@@ -760,13 +782,13 @@ void vkd3d_shader_parser_init(struct vkd3d_shader_parser *parser,
     parser->status = VKD3D_OK;
 }
 
-void VKD3D_PRINTF_FUNC(3, 4) vkd3d_shader_parser_error(struct vkd3d_shader_parser *parser,
-        enum vkd3d_shader_error error, const char *format, ...)
+void VKD3D_PRINTF_FUNC(4, 5) vkd3d_shader_parser_error_(struct vkd3d_shader_parser *parser,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_verror(parser->message_context, &parser->location, error, format, args);
+    vkd3d_shader_verror(parser->message_context, &parser->location, error, function, format, args);
     va_end(args);
 
     if (parser->status >= 0)
@@ -797,13 +819,13 @@ void VKD3D_PRINTF_FUNC(3, 4) vkd3d_shader_parser_error(struct vkd3d_shader_parse
     }
 }
 
-void VKD3D_PRINTF_FUNC(3, 4) vkd3d_shader_parser_warning(struct vkd3d_shader_parser *parser,
-        enum vkd3d_shader_error error, const char *format, ...)
+void VKD3D_PRINTF_FUNC(4, 5) vkd3d_shader_parser_warning_(struct vkd3d_shader_parser *parser,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_vwarning(parser->message_context, &parser->location, error, format, args);
+    vkd3d_shader_vwarning(parser->message_context, &parser->location, error, function, format, args);
     va_end(args);
 }
 
@@ -854,9 +876,19 @@ static enum vkd3d_result vsir_parse(const struct vkd3d_shader_compile_info *comp
         const struct shader_dump_data *dump_data, struct vkd3d_shader_message_context *message_context,
         struct vsir_program *program, struct vkd3d_shader_code *reflection_data)
 {
+    enum vkd3d_shader_api_version api_version = VKD3D_SHADER_API_VERSION_1_2;
     struct vkd3d_shader_compile_info preprocessed_info;
     struct vkd3d_shader_code preprocessed;
     enum vkd3d_result ret;
+    unsigned int i;
+
+    for (i = 0; i < compile_info->option_count; ++i)
+    {
+        const struct vkd3d_shader_compile_option *option = &compile_info->options[i];
+
+        if (option->name == VKD3D_SHADER_COMPILE_OPTION_API_VERSION)
+            api_version = option->value;
+    }
 
     switch (compile_info->source_type)
     {
@@ -897,6 +929,36 @@ static enum vkd3d_result vsir_parse(const struct vkd3d_shader_compile_info *comp
         return ret;
     }
 
+    if (api_version <= VKD3D_SHADER_API_VERSION_1_19)
+    {
+        program->f16_denormal_mode = VKD3D_SHADER_DENORMAL_MODE_ANY;
+        program->f32_denormal_mode = VKD3D_SHADER_DENORMAL_MODE_ANY;
+        program->f64_denormal_mode = VKD3D_SHADER_DENORMAL_MODE_ANY;
+    }
+
+    for (i = 0; i < compile_info->option_count; ++i)
+    {
+        const struct vkd3d_shader_compile_option *option = &compile_info->options[i];
+
+        switch (option->name)
+        {
+            case VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F16:
+                program->f16_denormal_mode = option->value;
+                break;
+
+            case VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F32:
+                program->f32_denormal_mode = option->value;
+                break;
+
+            case VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F64:
+                program->f64_denormal_mode = option->value;
+                break;
+
+            default:
+                break;
+        }
+    }
+
     if ((ret = vsir_program_validate(program, config_flags, compile_info->source_name, message_context)) < 0)
     {
         WARN("Failed to validate shader after parsing, ret %d.\n", ret);
@@ -1009,23 +1071,27 @@ struct vkd3d_shader_scan_context
     enum vkd3d_shader_tessellator_partitioning partitioning;
 };
 
-static VKD3D_PRINTF_FUNC(3, 4) void vkd3d_shader_scan_error(struct vkd3d_shader_scan_context *context,
-        enum vkd3d_shader_error error, const char *format, ...)
+#define vkd3d_shader_scan_error(context, error, ...) \
+        vkd3d_shader_scan_error_(context, error, __FUNCTION__, __VA_ARGS__)
+static VKD3D_PRINTF_FUNC(4, 5) void vkd3d_shader_scan_error_(struct vkd3d_shader_scan_context *context,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_verror(context->message_context, &context->location, error, format, args);
+    vkd3d_shader_verror(context->message_context, &context->location, error, function, format, args);
     va_end(args);
 }
 
-static void VKD3D_PRINTF_FUNC(3, 4) vkd3d_shader_scan_warning(struct vkd3d_shader_scan_context *context,
-        enum vkd3d_shader_error error, const char *format, ...)
+#define vkd3d_shader_scan_warning(context, error, ...) \
+        vkd3d_shader_scan_warning_(context, error, __FUNCTION__, __VA_ARGS__)
+static void VKD3D_PRINTF_FUNC(4, 5) vkd3d_shader_scan_warning_(struct vkd3d_shader_scan_context *context,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...)
 {
     va_list args;
 
     va_start(args, format);
-    vkd3d_shader_vwarning(context->message_context, &context->location, error, format, args);
+    vkd3d_shader_vwarning(context->message_context, &context->location, error, function, format, args);
     va_end(args);
 }
 
@@ -1128,7 +1194,7 @@ static struct vkd3d_shader_cf_info *vkd3d_shader_scan_find_innermost_loop_cf_inf
 }
 
 static void vkd3d_shader_scan_add_uav_flag(const struct vkd3d_shader_scan_context *context,
-        const struct vkd3d_shader_register *reg, uint32_t flag)
+        const struct vsir_operand *reg, uint32_t flag)
 {
     unsigned int range_id = reg->idx[0].offset;
     unsigned int i;
@@ -1159,7 +1225,7 @@ static bool vkd3d_shader_instruction_is_uav_read(const struct vkd3d_shader_instr
 }
 
 static void vkd3d_shader_scan_record_uav_read(struct vkd3d_shader_scan_context *context,
-        const struct vkd3d_shader_register *reg)
+        const struct vsir_operand *reg)
 {
     vkd3d_shader_scan_add_uav_flag(context, reg, VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_UAV_READ);
 }
@@ -1172,7 +1238,7 @@ static bool vkd3d_shader_instruction_is_uav_counter(const struct vkd3d_shader_in
 }
 
 static void vkd3d_shader_scan_record_uav_counter(struct vkd3d_shader_scan_context *context,
-        const struct vkd3d_shader_register *reg)
+        const struct vsir_operand *reg)
 {
     vkd3d_shader_scan_add_uav_flag(context, reg, VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_UAV_COUNTER);
 }
@@ -1186,14 +1252,14 @@ static bool vkd3d_shader_instruction_is_uav_atomic_op(const struct vkd3d_shader_
 }
 
 static void vkd3d_shader_scan_record_uav_atomic_op(struct vkd3d_shader_scan_context *context,
-        const struct vkd3d_shader_register *reg)
+        const struct vsir_operand *reg)
 {
     vkd3d_shader_scan_add_uav_flag(context, reg, VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_UAV_ATOMICS);
 }
 
 static struct vkd3d_shader_descriptor_info1 *vkd3d_shader_scan_add_descriptor(struct vkd3d_shader_scan_context *context,
-        enum vkd3d_shader_descriptor_type type, const struct vkd3d_shader_register *reg,
-        const struct vkd3d_shader_register_range *range, enum vkd3d_shader_resource_type resource_type,
+        enum vkd3d_shader_descriptor_type type, const struct vsir_operand *reg,
+        const struct vsir_register_range *range, enum vkd3d_shader_resource_type resource_type,
         enum vsir_data_type resource_data_type)
 {
     struct vkd3d_shader_scan_descriptor_info1 *info = context->scan_descriptor_info;
@@ -1265,7 +1331,7 @@ const struct vkd3d_shader_descriptor_info1 *vkd3d_shader_find_descriptor(
 }
 
 static void vkd3d_shader_scan_combined_sampler_usage(struct vkd3d_shader_scan_context *context,
-        const struct vkd3d_shader_register *resource, const struct vkd3d_shader_register *sampler)
+        const struct vsir_operand *resource, const struct vsir_operand *sampler)
 {
     struct vkd3d_shader_scan_combined_resource_sampler_info *info;
     struct vkd3d_shader_combined_resource_sampler_info *s;
@@ -1376,7 +1442,7 @@ static void vkd3d_shader_scan_typed_resource_declaration(struct vkd3d_shader_sca
 static int vkd3d_shader_scan_instruction(struct vkd3d_shader_scan_context *context,
         const struct vkd3d_shader_instruction *instruction)
 {
-    const struct vkd3d_shader_register *sampler_reg;
+    const struct vsir_operand *sampler_reg;
     struct vkd3d_shader_cf_info *cf_info;
     unsigned int i;
 
@@ -1702,6 +1768,7 @@ static int vsir_program_scan(struct vsir_program *program, const struct vkd3d_sh
     struct vkd3d_shader_scan_combined_resource_sampler_info *combined_sampler_info;
     struct vkd3d_shader_scan_hull_shader_tessellation_info *tessellation_info;
     struct vkd3d_shader_scan_thread_group_size_info *thread_group_size_info;
+    struct vkd3d_shader_scan_denormal_mode_info *denormal_mode_info;
     struct vkd3d_shader_scan_descriptor_info *descriptor_info;
     struct vkd3d_shader_scan_signature_info *signature_info;
     struct vkd3d_shader_scan_context context;
@@ -1726,6 +1793,7 @@ static int vsir_program_scan(struct vsir_program *program, const struct vkd3d_sh
 
     tessellation_info = vkd3d_find_struct(compile_info->next, SCAN_HULL_SHADER_TESSELLATION_INFO);
     thread_group_size_info = vkd3d_find_struct(compile_info->next, SCAN_THREAD_GROUP_SIZE_INFO);
+    denormal_mode_info = vkd3d_find_struct(compile_info->next, SCAN_DENORMAL_MODE_INFO);
 
     vkd3d_shader_scan_context_init(&context, &program->shader_version, compile_info,
             add_descriptor_info ? &program->descriptors : NULL, combined_sampler_info, message_context);
@@ -1770,6 +1838,13 @@ static int vsir_program_scan(struct vsir_program *program, const struct vkd3d_sh
         thread_group_size_info->z = program->thread_group_size.z;
     }
 
+    if (!ret && denormal_mode_info)
+    {
+        denormal_mode_info->f16_denormal_mode = program->f16_denormal_mode;
+        denormal_mode_info->f32_denormal_mode = program->f32_denormal_mode;
+        denormal_mode_info->f64_denormal_mode = program->f64_denormal_mode;
+    }
+
     if (ret < 0)
     {
         if (combined_sampler_info)
diff --git a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h
index 7c278a63b808..755f53570226 100644
--- a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h
+++ b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h
@@ -55,6 +55,10 @@
 #include <limits.h>
 #include <stdbool.h>
 #include <string.h>
+#ifdef HAVE_XLOCALE_H
+#include <xlocale.h>
+#endif
+#include <locale.h>
 
 #define VKD3D_VEC4_SIZE 4
 #define VKD3D_DVEC2_SIZE 2
@@ -189,6 +193,7 @@ enum vkd3d_shader_error
     VKD3D_SHADER_WARNING_HLSL_IGNORED_DEFAULT_VALUE     = 5306,
     VKD3D_SHADER_WARNING_HLSL_IGNORED_MODIFIER          = 5307,
     VKD3D_SHADER_WARNING_HLSL_OVERRIDDEN_SEMANTIC       = 5308,
+    VKD3D_SHADER_WARNING_HLSL_REDEFINED                 = 5309,
 
     VKD3D_SHADER_ERROR_GLSL_INTERNAL                    = 6000,
     VKD3D_SHADER_ERROR_GLSL_BINDING_NOT_FOUND           = 6001,
@@ -279,6 +284,7 @@ enum vkd3d_shader_error
     VKD3D_SHADER_ERROR_VSIR_INVALID_RESOURCE_TYPE       = 9025,
     VKD3D_SHADER_ERROR_VSIR_INVALID_DESCRIPTOR_COUNT    = 9026,
     VKD3D_SHADER_ERROR_VSIR_INVALID_FLAGS               = 9027,
+    VKD3D_SHADER_ERROR_VSIR_MULTIPLE_SRC_OPERAND_OWNERS = 9028,
 
     VKD3D_SHADER_WARNING_VSIR_DYNAMIC_DESCRIPTOR_ARRAY  = 9300,
 
@@ -728,6 +734,8 @@ enum vkd3d_shader_register_type
     VKD3DSPR_INVALID = ~0u,
 };
 
+const char *vsir_register_type_get_name(enum vkd3d_shader_register_type type, const char *error);
+
 enum vsir_rastout_register
 {
     VSIR_RASTOUT_POSITION   = 0x0,
@@ -872,7 +880,7 @@ enum vsir_global_flags
     VKD3DSGF_SKIP_OPTIMIZATION                 = 0x10,
     VKD3DSGF_ENABLE_MINIMUM_PRECISION          = 0x20,
     VKD3DSGF_ENABLE_11_1_DOUBLE_EXTENSIONS     = 0x40,
-    VKD3DSGF_ENABLE_SHADER_EXTENSIONS          = 0x80, /* never emitted? */
+    VKD3DSGF_ENABLE_11_1_SHADER_EXTENSIONS     = 0x80,
     VKD3DSGF_BIND_FOR_DURATION                 =     0x100,
     VKD3DSGF_ENABLE_VP_AND_RT_ARRAY_INDEX      =     0x200,
     VKD3DSGF_ENABLE_INNER_COVERAGE             =     0x400,
@@ -888,7 +896,7 @@ enum vsir_global_flags
     VKD3DSGF_ENABLE_INT64                      =  0x100000,
     VKD3DSGF_ENABLE_VIEWID                     =  0x200000,
     VKD3DSGF_ENABLE_BARYCENTRICS               =  0x400000,
-    VKD3DSGF_FORCE_NATIVE_LOW_PRECISION        =  0x800000,
+    VKD3DSGF_ENABLE_NATIVE_LOW_PRECISION       =  0x800000,
     VKD3DSGF_ENABLE_SHADINGRATE                = 0x1000000,
     VKD3DSGF_ENABLE_RAYTRACING_TIER_1_1        = 0x2000000,
     VKD3DSGF_ENABLE_SAMPLER_FEEDBACK           = 0x4000000,
@@ -1033,7 +1041,9 @@ struct vkd3d_shader_indexable_temp
     const struct vkd3d_shader_immediate_constant_buffer *initialiser;
 };
 
-struct vkd3d_shader_register_index
+/* This structure is used by vsir_register_index_compare(); changes to the
+ * structure should be reflected by the comparison function as well. */
+struct vsir_register_index
 {
     struct vsir_src_operand *rel_addr;
     unsigned int offset;
@@ -1041,13 +1051,15 @@ struct vkd3d_shader_register_index
     bool is_in_bounds;
 };
 
-struct vkd3d_shader_register
+/* This structure is used by vsir_operand_compare(); changes to the structure
+ * should be reflected by the comparison function as well. */
+struct vsir_operand
 {
     enum vkd3d_shader_register_type type;
     enum vkd3d_shader_register_precision precision;
     bool non_uniform;
     enum vsir_data_type data_type;
-    struct vkd3d_shader_register_index idx[3];
+    struct vsir_register_index idx[3];
     unsigned int idx_count;
     enum vsir_dimension dimension;
     /* known address alignment for optimisation, or zero */
@@ -1062,10 +1074,10 @@ struct vkd3d_shader_register
     } u;
 };
 
-void vsir_register_init(struct vkd3d_shader_register *reg, enum vkd3d_shader_register_type reg_type,
+void vsir_operand_init(struct vsir_operand *reg, enum vkd3d_shader_register_type reg_type,
         enum vsir_data_type data_type, unsigned int idx_count);
 
-static inline bool vsir_register_is_descriptor(const struct vkd3d_shader_register *reg)
+static inline bool vsir_operand_is_descriptor(const struct vsir_operand *reg)
 {
     switch (reg->type)
     {
@@ -1080,6 +1092,31 @@ static inline bool vsir_register_is_descriptor(const struct vkd3d_shader_registe
     }
 }
 
+static inline bool vsir_operand_is_constant(const struct vsir_operand *reg)
+{
+    return (reg->type == VKD3DSPR_IMMCONST || reg->type == VKD3DSPR_IMMCONST64);
+}
+
+static inline bool vsir_operand_is_undef(const struct vsir_operand *reg)
+{
+    return reg->type == VKD3DSPR_UNDEF;
+}
+
+static inline bool vsir_operand_is_constant_or_undef(const struct vsir_operand *reg)
+{
+    return vsir_operand_is_constant(reg) || vsir_operand_is_undef(reg);
+}
+
+static inline bool vsir_operand_is_label(const struct vsir_operand *reg)
+{
+    return reg->type == VKD3DSPR_LABEL;
+}
+
+static inline bool vsir_operand_is_ssa(const struct vsir_operand *reg)
+{
+    return reg->type == VKD3DSPR_SSA;
+}
+
 static inline enum vkd3d_shader_register_type vsir_register_type_from_sysval_input(
         enum vkd3d_shader_sysval_semantic sysval)
 {
@@ -1094,9 +1131,11 @@ static inline enum vkd3d_shader_register_type vsir_register_type_from_sysval_inp
     }
 }
 
+/* This structure is used by vsir_cse_expr_key_compare(); changes to the
+ * structure should be reflected by that function as well. */
 struct vsir_dst_operand
 {
-    struct vkd3d_shader_register reg;
+    struct vsir_operand reg;
     uint32_t write_mask;
     uint32_t modifiers;
     unsigned int shift;
@@ -1105,17 +1144,24 @@ struct vsir_dst_operand
 void vsir_dst_operand_init(struct vsir_dst_operand *dst, enum vkd3d_shader_register_type reg_type,
         enum vsir_data_type data_type, unsigned int idx_count);
 void vsir_dst_operand_init_null(struct vsir_dst_operand *dst);
+void vsir_dst_operand_init_ssa_f32v4(struct vsir_dst_operand *dst, unsigned int idx);
 
+/* This structure is used by vsir_src_operand_compare(); changes to the
+ * structure should be reflected by the comparison function as well. */
 struct vsir_src_operand
 {
-    struct vkd3d_shader_register reg;
+    struct vsir_operand reg;
     uint32_t swizzle;
     enum vkd3d_shader_src_modifier modifiers;
+
+    /* Index of the instruction that uses the src operand, only used during validation. */
+    size_t owner_index;
 };
 
 void vsir_src_operand_init(struct vsir_src_operand *src, enum vkd3d_shader_register_type reg_type,
         enum vsir_data_type data_type, unsigned int idx_count);
 void vsir_src_operand_init_label(struct vsir_src_operand *src, unsigned int label_id);
+void vsir_src_operand_init_ssa_f32v4(struct vsir_src_operand *src, unsigned int idx);
 
 struct vkd3d_shader_index_range
 {
@@ -1123,7 +1169,7 @@ struct vkd3d_shader_index_range
     unsigned int register_count;
 };
 
-struct vkd3d_shader_register_range
+struct vsir_register_range
 {
     unsigned int space;
     unsigned int first, last;
@@ -1132,7 +1178,7 @@ struct vkd3d_shader_register_range
 struct vkd3d_shader_resource
 {
     struct vsir_dst_operand reg;
-    struct vkd3d_shader_register_range range;
+    struct vsir_register_range range;
 };
 
 enum vkd3d_decl_usage
@@ -1288,14 +1334,14 @@ struct vkd3d_shader_register_semantic
 struct vkd3d_shader_sampler
 {
     struct vsir_src_operand src;
-    struct vkd3d_shader_register_range range;
+    struct vsir_register_range range;
 };
 
 struct vkd3d_shader_constant_buffer
 {
     struct vsir_src_operand src;
     unsigned int size;
-    struct vkd3d_shader_register_range range;
+    struct vsir_register_range range;
 };
 
 struct vkd3d_shader_structured_resource
@@ -1380,6 +1426,8 @@ struct vkd3d_shader_location
     unsigned int line, column;
 };
 
+/* This structure is used by vsir_cse_expr_key_compare(); changes to the
+ * structure should be reflected by that function as well. */
 struct vkd3d_shader_instruction
 {
     struct vkd3d_shader_location location;
@@ -1447,37 +1495,6 @@ static inline bool vkd3d_shader_instruction_has_texel_offset(const struct vkd3d_
     return ins->texel_offset.u || ins->texel_offset.v || ins->texel_offset.w;
 }
 
-static inline bool register_is_constant(const struct vkd3d_shader_register *reg)
-{
-    return (reg->type == VKD3DSPR_IMMCONST || reg->type == VKD3DSPR_IMMCONST64);
-}
-
-static inline bool register_is_undef(const struct vkd3d_shader_register *reg)
-{
-    return reg->type == VKD3DSPR_UNDEF;
-}
-
-static inline bool register_is_constant_or_undef(const struct vkd3d_shader_register *reg)
-{
-    return register_is_constant(reg) || register_is_undef(reg);
-}
-
-static inline bool register_is_numeric_array(const struct vkd3d_shader_register *reg)
-{
-    return (reg->type == VKD3DSPR_IMMCONSTBUFFER || reg->type == VKD3DSPR_IDXTEMP
-            || reg->type == VKD3DSPR_GROUPSHAREDMEM);
-}
-
-static inline bool vsir_register_is_label(const struct vkd3d_shader_register *reg)
-{
-    return reg->type == VKD3DSPR_LABEL;
-}
-
-static inline bool register_is_ssa(const struct vkd3d_shader_register *reg)
-{
-    return reg->type == VKD3DSPR_SSA;
-}
-
 struct vkd3d_shader_param_node
 {
     struct vkd3d_shader_param_node *next;
@@ -1638,14 +1655,7 @@ enum vsir_normalisation_level
     VSIR_NORMALISED_SM6,
 };
 
-enum vsir_denorm_mode
-{
-    VSIR_DENORM_ANY = 0,
-    VSIR_DENORM_PRESERVE,
-    VSIR_DENORM_FLUSH_TO_ZERO,
-};
-
-const char *vsir_denorm_mode_get_name(enum vsir_denorm_mode m, const char *error);
+const char *vsir_denorm_mode_get_name(enum vkd3d_shader_denormal_mode m, const char *error);
 
 struct vkd3d_shader_descriptor_info1
 {
@@ -1726,9 +1736,9 @@ struct vsir_program
     struct vkd3d_shader_param_allocator src_operands;
     struct vkd3d_shader_param_allocator dst_operands;
 
-    enum vsir_denorm_mode f16_denorm_mode;
-    enum vsir_denorm_mode f32_denorm_mode;
-    enum vsir_denorm_mode f64_denorm_mode;
+    enum vkd3d_shader_denormal_mode f16_denormal_mode;
+    enum vkd3d_shader_denormal_mode f32_denormal_mode;
+    enum vkd3d_shader_denormal_mode f64_denormal_mode;
 };
 
 enum vkd3d_result vsir_allocate_temp_registers(struct vsir_program *program,
@@ -1737,8 +1747,7 @@ enum vkd3d_result vsir_update_dcl_temps(struct vsir_program *program,
         struct vkd3d_shader_message_context *message_context);
 
 struct vkd3d_shader_descriptor_info1 *vsir_program_add_descriptor(struct vsir_program *program,
-        enum vkd3d_shader_descriptor_type type, unsigned int register_id,
-        const struct vkd3d_shader_register_range *range,
+        enum vkd3d_shader_descriptor_type type, unsigned int register_id, const struct vsir_register_range *range,
         enum vkd3d_shader_resource_type resource_type, enum vsir_data_type resource_data_type);
 bool vsir_program_add_icb(struct vsir_program *program, struct vkd3d_shader_immediate_constant_buffer *icb);
 void vsir_program_cleanup(struct vsir_program *program);
@@ -1797,12 +1806,16 @@ struct vkd3d_shader_parser
     enum vkd3d_result status;
 };
 
-void vkd3d_shader_parser_error(struct vkd3d_shader_parser *parser,
-        enum vkd3d_shader_error error, const char *format, ...) VKD3D_PRINTF_FUNC(3, 4);
+#define vkd3d_shader_parser_error(parser, error, ...) \
+        vkd3d_shader_parser_error_(parser, error, __FUNCTION__, __VA_ARGS__)
+void vkd3d_shader_parser_error_(struct vkd3d_shader_parser *parser, enum vkd3d_shader_error error,
+        const char *function, const char *format, ...) VKD3D_PRINTF_FUNC(4, 5);
 void vkd3d_shader_parser_init(struct vkd3d_shader_parser *parser,
         struct vkd3d_shader_message_context *message_context, const char *source_name);
-void vkd3d_shader_parser_warning(struct vkd3d_shader_parser *parser,
-        enum vkd3d_shader_error error, const char *format, ...) VKD3D_PRINTF_FUNC(3, 4);
+#define vkd3d_shader_parser_warning(parser, error, ...) \
+        vkd3d_shader_parser_warning_(parser, error, __FUNCTION__, __VA_ARGS__)
+void vkd3d_shader_parser_warning_(struct vkd3d_shader_parser *parser, enum vkd3d_shader_error error,
+        const char *function, const char *format, ...) VKD3D_PRINTF_FUNC(4, 5);
 
 void vsir_program_trace(struct vsir_program *program);
 
@@ -1841,6 +1854,7 @@ void vkd3d_string_buffer_cache_cleanup(struct vkd3d_string_buffer_cache *list);
 void vkd3d_string_buffer_cache_init(struct vkd3d_string_buffer_cache *list);
 void vkd3d_string_buffer_clear(struct vkd3d_string_buffer *buffer);
 void vkd3d_string_buffer_truncate(struct vkd3d_string_buffer *buffer, size_t size);
+int vkd3d_string_buffer_print_f16(struct vkd3d_string_buffer *buffer, uint16_t f);
 int vkd3d_string_buffer_print_f32(struct vkd3d_string_buffer *buffer, float f);
 int vkd3d_string_buffer_print_f64(struct vkd3d_string_buffer *buffer, double d);
 int vkd3d_string_buffer_print_string_escaped(struct vkd3d_string_buffer *buffer, const char *s, size_t len);
@@ -1889,6 +1903,13 @@ static inline size_t bytecode_get_size(struct vkd3d_bytecode_buffer *buffer)
     return buffer->size;
 }
 
+#ifdef _WIN32
+# define vkd3d_locale _locale_t
+#else
+# define vkd3d_locale locale_t
+#endif
+
+float vkd3d_parse_float(const char *s, vkd3d_locale l);
 uint32_t vkd3d_parse_integer(const char *s);
 
 struct vkd3d_shader_message_context
@@ -1904,23 +1925,24 @@ void vkd3d_shader_message_context_trace_messages_(const struct vkd3d_shader_mess
         const char *function);
 #define vkd3d_shader_message_context_trace_messages(context) \
         vkd3d_shader_message_context_trace_messages_(context, __FUNCTION__)
-void vkd3d_shader_error(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
-        enum vkd3d_shader_error error, const char *format, ...) VKD3D_PRINTF_FUNC(4, 5);
+#define vkd3d_shader_error(context, location, error, ...) \
+        vkd3d_shader_error_(context, location, error, __FUNCTION__, __VA_ARGS__)
+void vkd3d_shader_error_(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...) VKD3D_PRINTF_FUNC(5, 6);
 void vkd3d_shader_verror(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
-        enum vkd3d_shader_error error, const char *format, va_list args);
+        enum vkd3d_shader_error error, const char *function, const char *format, va_list args);
 void vkd3d_shader_vnote(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
-        enum vkd3d_shader_log_level level, const char *format, va_list args);
-void vkd3d_shader_warning(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
-        enum vkd3d_shader_error error, const char *format, ...) VKD3D_PRINTF_FUNC(4, 5);
+        enum vkd3d_shader_log_level level, const char *function, const char *format, va_list args);
+#define vkd3d_shader_warning(context, location, error, ...) \
+        vkd3d_shader_warning_(context, location, error, __FUNCTION__, __VA_ARGS__)
+void vkd3d_shader_warning_(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
+        enum vkd3d_shader_error error, const char *function, const char *format, ...) VKD3D_PRINTF_FUNC(5, 6);
 void vkd3d_shader_vwarning(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
-        enum vkd3d_shader_error error, const char *format, va_list args);
+        enum vkd3d_shader_error error, const char *function, const char *format, va_list args);
 
 void vkd3d_shader_string_from_message_context(char **out, struct vkd3d_shader_message_context *context);
 
 uint64_t vkd3d_shader_init_config_flags(void);
-void vkd3d_shader_trace_text_(const char *text, size_t size, const char *function);
-#define vkd3d_shader_trace_text(text, size) \
-        vkd3d_shader_trace_text_(text, size, __FUNCTION__)
 
 bool sm1_register_from_semantic_name(const struct vkd3d_shader_version *version, const char *semantic_name,
         unsigned int semantic_index, bool output, enum vkd3d_shader_sysval_semantic *sysval,
@@ -1929,11 +1951,12 @@ bool sm1_usage_from_semantic_name(const char *semantic_name,
         uint32_t semantic_index, enum vkd3d_decl_usage *usage, uint32_t *usage_idx);
 bool sm4_register_from_semantic_name(const struct vkd3d_shader_version *version,
         const char *semantic_name, bool output, enum vkd3d_shader_register_type *type, bool *has_idx);
-bool shader_sm4_is_scalar_register(const struct vkd3d_shader_register *reg);
+bool shader_sm4_is_scalar_register(const struct vsir_operand *reg);
 bool sm4_sysval_semantic_from_semantic_name(enum vkd3d_shader_sysval_semantic *sysval_semantic,
-        const struct vkd3d_shader_version *version, bool semantic_compat_mapping, enum vkd3d_tessellator_domain domain,
-        const char *semantic_name, unsigned int semantic_idx, bool output,
-        bool is_patch_constant_func, bool is_primitive);
+        const struct vkd3d_shader_version *version,
+        enum vkd3d_shader_compile_option_backward_compatibility compatibility_flags,
+        enum vkd3d_tessellator_domain domain, const char *semantic_name,
+        unsigned int semantic_idx, bool output, bool is_patch_constant_func, bool is_primitive);
 
 int d3dbc_parse(const struct vkd3d_shader_compile_info *compile_info, uint64_t config_flags,
         struct vkd3d_shader_message_context *message_context, struct vsir_program *program);
diff --git a/libs/vkd3d/libs/vkd3d-utils/vkd3d_utils_main.c b/libs/vkd3d/libs/vkd3d-utils/vkd3d_utils_main.c
index 6e90b48e8775..c75418ae2543 100644
--- a/libs/vkd3d/libs/vkd3d-utils/vkd3d_utils_main.c
+++ b/libs/vkd3d/libs/vkd3d-utils/vkd3d_utils_main.c
@@ -16,7 +16,14 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "config.h"
+#include <unistd.h>
+#ifdef _WIN32
+#include <direct.h>     /* For getcwd() */
+#endif
 #include "vkd3d_utils_private.h"
+#include "vkd3d_shader_utils.h"
+
 #undef D3D12CreateDevice
 
 static const char *debug_d3d_blob_part(D3D_BLOB_PART part)
@@ -157,6 +164,93 @@ HRESULT WINAPI D3D12SerializeVersionedRootSignature(const D3D12_VERSIONED_ROOT_S
     return vkd3d_serialize_versioned_root_signature(desc, blob, error_blob);
 }
 
+struct standard_include_ctx
+{
+    char *initial_dir;
+    size_t len;
+};
+
+static enum vkd3d_result standard_include_init(struct standard_include_ctx *ctx, const char *initial_filename)
+{
+    const char *last_slash = NULL;
+    const char *ptr;
+
+    for (ptr = initial_filename; *ptr; ++ptr)
+    {
+#ifdef _WIN32
+        if (*ptr == '/' || *ptr == '\\')
+#else
+        if (*ptr == '/')
+#endif
+            last_slash = ptr;
+    }
+
+    if (last_slash)
+    {
+        ctx->len = last_slash - initial_filename + 1;
+        if (!(ctx->initial_dir = vkd3d_malloc(ctx->len)))
+            return VKD3D_ERROR_OUT_OF_MEMORY;
+
+        memcpy(ctx->initial_dir, initial_filename, ctx->len);
+
+        /* Normalize. */
+        ctx->initial_dir[ctx->len - 1] = '/';
+
+        return VKD3D_OK;
+    }
+
+    if ((ctx->initial_dir = getcwd(NULL, 0)))
+    {
+        ctx->len = strlen(ctx->initial_dir);
+
+        /* Check for empty or / cwd. */
+        if (ctx->len && ctx->initial_dir[ctx->len - 1] != '/')
+        {
+            /* Append, overwriting the null terminator intentionally. */
+            ctx->initial_dir[ctx->len] = '/';
+            ctx->len++;
+        }
+
+        return VKD3D_OK;
+    }
+
+    /* Fallback to relative path. */
+    ctx->len = 2;
+    if (!(ctx->initial_dir = vkd3d_malloc(ctx->len)))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+    ctx->initial_dir[0] = '.';
+    ctx->initial_dir[1] = '/';
+
+    return VKD3D_OK;
+}
+
+static enum vkd3d_result standard_include_open(const char *filename, bool local,
+        const char *parent_data, void *context, struct vkd3d_shader_code *out)
+{
+    struct standard_include_ctx *ctx = context;
+    enum vkd3d_result res;
+    char *full_path;
+    size_t len;
+    FILE *file;
+
+    len = strlen(filename);
+    if (!(full_path = vkd3d_malloc(ctx->len + len + 1)))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+    memcpy(full_path, ctx->initial_dir, ctx->len);
+    memcpy(&full_path[ctx->len], filename, len + 1);
+    file = fopen(full_path, "rb");
+    vkd3d_free(full_path);
+
+    /* d3dcompiler tries with the prefix path and direct filename. */
+    if (!file && !(file = fopen(filename, "rb")))
+        return VKD3D_ERROR;
+
+    res = vkd3d_shader_code_from_file(out, file);
+    fclose(file);
+
+    return res;
+}
+
 static int open_include(const char *filename, bool local, const char *parent_data, void *context,
         struct vkd3d_shader_code *code)
 {
@@ -179,7 +273,8 @@ static void close_include(const struct vkd3d_shader_code *code, void *context)
 {
     ID3DInclude *iface = context;
 
-    ID3DInclude_Close(iface, code->code);
+    if (code->code)
+        ID3DInclude_Close(iface, code->code);
 }
 
 static enum vkd3d_shader_target_type get_target_for_profile(const char *profile)
@@ -244,6 +339,7 @@ HRESULT WINAPI D3DCompile2VKD3D(const void *data, SIZE_T data_size, const char *
     struct vkd3d_shader_compile_option options[7];
     struct vkd3d_shader_compile_info compile_info;
     struct vkd3d_shader_compile_option *option;
+    struct standard_include_ctx ctx = { 0 };
     struct vkd3d_shader_code byte_code;
     const D3D_SHADER_MACRO *macro;
     char *messages;
@@ -291,9 +387,22 @@ HRESULT WINAPI D3DCompile2VKD3D(const void *data, SIZE_T data_size, const char *
         for (macro = macros; macro->Name; ++macro)
             ++preprocess_info.macro_count;
     }
-    preprocess_info.pfn_open_include = open_include;
-    preprocess_info.pfn_close_include = close_include;
-    preprocess_info.include_context = include;
+
+    if (include == D3D_COMPILE_STANDARD_FILE_INCLUDE)
+    {
+        if ((ret = standard_include_init(&ctx, filename)) != VKD3D_OK)
+            return hresult_from_vkd3d_result(ret);
+
+        preprocess_info.pfn_open_include = standard_include_open;
+        preprocess_info.pfn_close_include = NULL;
+        preprocess_info.include_context = &ctx;
+    }
+    else
+    {
+        preprocess_info.pfn_open_include = open_include;
+        preprocess_info.pfn_close_include = close_include;
+        preprocess_info.include_context = include;
+    }
 
     hlsl_info.type = VKD3D_SHADER_STRUCTURE_TYPE_HLSL_SOURCE_INFO;
     hlsl_info.next = NULL;
@@ -320,12 +429,13 @@ HRESULT WINAPI D3DCompile2VKD3D(const void *data, SIZE_T data_size, const char *
             option->value |= VKD3D_SHADER_COMPILE_OPTION_PACK_MATRIX_COLUMN_MAJOR;
     }
 
+    option = &options[compile_info.option_count++];
+    option->name = VKD3D_SHADER_COMPILE_OPTION_BACKWARD_COMPATIBILITY;
+    option->value = 0;
     if (flags & D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY)
-    {
-        option = &options[compile_info.option_count++];
-        option->name = VKD3D_SHADER_COMPILE_OPTION_BACKWARD_COMPATIBILITY;
         option->value = VKD3D_SHADER_COMPILE_OPTION_BACKCOMPAT_MAP_SEMANTIC_NAMES;
-    }
+    else if (compiler_version >= 37)
+        option->value = VKD3D_SHADER_COMPILE_OPTION_CONST_GLOBAL_UNIFORMS;
 
     if (effect_flags & D3DCOMPILE_EFFECT_CHILD_EFFECT)
     {
@@ -350,6 +460,9 @@ HRESULT WINAPI D3DCompile2VKD3D(const void *data, SIZE_T data_size, const char *
 
     ret = vkd3d_shader_compile(&compile_info, &byte_code, &messages);
 
+    if (ctx.initial_dir)
+        vkd3d_free(ctx.initial_dir);
+
     if (messages && messages_blob)
     {
         if (FAILED(hr = vkd3d_blob_create(messages, strlen(messages), messages_blob)))
@@ -426,6 +539,7 @@ HRESULT WINAPI D3DPreprocess(const void *data, SIZE_T size, const char *filename
     struct vkd3d_shader_preprocess_info preprocess_info;
     struct vkd3d_shader_compile_info compile_info;
     struct vkd3d_shader_code preprocessed_code;
+    struct standard_include_ctx ctx = { 0 };
     const D3D_SHADER_MACRO *macro;
     char *messages;
     HRESULT hr;
@@ -462,12 +576,28 @@ HRESULT WINAPI D3DPreprocess(const void *data, SIZE_T size, const char *filename
         for (macro = macros; macro->Name; ++macro)
             ++preprocess_info.macro_count;
     }
-    preprocess_info.pfn_open_include = open_include;
-    preprocess_info.pfn_close_include = close_include;
-    preprocess_info.include_context = include;
+
+    if (include == D3D_COMPILE_STANDARD_FILE_INCLUDE)
+    {
+        if ((ret = standard_include_init(&ctx, filename)) != VKD3D_OK)
+            return hresult_from_vkd3d_result(ret);
+
+        preprocess_info.pfn_open_include = standard_include_open;
+        preprocess_info.pfn_close_include = NULL;
+        preprocess_info.include_context = &ctx;
+    }
+    else
+    {
+        preprocess_info.pfn_open_include = open_include;
+        preprocess_info.pfn_close_include = close_include;
+        preprocess_info.include_context = include;
+    }
 
     ret = vkd3d_shader_preprocess(&compile_info, &preprocessed_code, &messages);
 
+    if (ctx.initial_dir)
+        vkd3d_free(ctx.initial_dir);
+
     if (messages && messages_blob)
     {
         if (FAILED(hr = vkd3d_blob_create(messages, strlen(messages), messages_blob)))
diff --git a/libs/vkd3d/libs/vkd3d/command.c b/libs/vkd3d/libs/vkd3d/command.c
index 84407e05cdb4..0ece8fdb82a7 100644
--- a/libs/vkd3d/libs/vkd3d/command.c
+++ b/libs/vkd3d/libs/vkd3d/command.c
@@ -4158,6 +4158,7 @@ static void STDMETHODCALLTYPE d3d12_command_list_CopyResource(ID3D12GraphicsComm
     struct d3d12_resource *dst_resource, *src_resource;
     const struct vkd3d_format *dst_format, *src_format;
     const struct vkd3d_vk_device_procs *vk_procs;
+    VkImageAspectFlags vk_aspect_mask;
     VkBufferCopy vk_buffer_copy;
     VkImageCopy vk_image_copy;
     unsigned int layer_count;
@@ -4197,7 +4198,11 @@ static void STDMETHODCALLTYPE d3d12_command_list_CopyResource(ID3D12GraphicsComm
         VKD3D_ASSERT(dst_resource->desc.MipLevels == src_resource->desc.MipLevels);
         VKD3D_ASSERT(layer_count == d3d12_resource_desc_get_layer_count(&src_resource->desc));
 
-        if (src_format->vk_aspect_mask != dst_format->vk_aspect_mask)
+        /* E.g., for D32_FLOAT_S8X24_UINT -> X32_TYPELESS_G8X24_UINT we just
+         * need to copy the STENCIL aspect. For D32_FLOAT -> R32_FLOAT we need
+         * to do a DEPTH -> COLOR copy. */
+        vk_aspect_mask = src_format->vk_aspect_mask & dst_format->vk_aspect_mask;
+        if (vk_aspect_mask != dst_format->vk_aspect_mask)
         {
             for (i = 0; i < dst_resource->desc.MipLevels; ++i)
             {
@@ -4212,8 +4217,10 @@ static void STDMETHODCALLTYPE d3d12_command_list_CopyResource(ID3D12GraphicsComm
         {
             vk_image_copy_from_d3d12(&vk_image_copy, i, i, &src_resource->desc, &dst_resource->desc,
                     src_format, dst_format, NULL, 0, 0, 0);
-            vk_image_copy.dstSubresource.layerCount = layer_count;
+            vk_image_copy.srcSubresource.aspectMask = vk_aspect_mask;
             vk_image_copy.srcSubresource.layerCount = layer_count;
+            vk_image_copy.dstSubresource.aspectMask = vk_aspect_mask;
+            vk_image_copy.dstSubresource.layerCount = layer_count;
             VK_CALL(vkCmdCopyImage(list->vk_command_buffer, src_resource->u.vk_image,
                     VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_resource->u.vk_image,
                     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &vk_image_copy));
diff --git a/libs/vkd3d/libs/vkd3d/state.c b/libs/vkd3d/libs/vkd3d/state.c
index d733165312cc..44f58349dce3 100644
--- a/libs/vkd3d/libs/vkd3d/state.c
+++ b/libs/vkd3d/libs/vkd3d/state.c
@@ -2395,6 +2395,9 @@ static HRESULT create_shader_stage(struct d3d12_device *device,
         {VKD3D_SHADER_COMPILE_OPTION_TYPED_UAV, typed_uav_compile_option(device)},
         {VKD3D_SHADER_COMPILE_OPTION_WRITE_TESS_GEOM_POINT_SIZE, 0},
         {VKD3D_SHADER_COMPILE_OPTION_FEATURE, feature_flags_compile_option(device)},
+        {VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F16, VKD3D_SHADER_DENORMAL_MODE_ANY},
+        {VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F32, VKD3D_SHADER_DENORMAL_MODE_ANY},
+        {VKD3D_SHADER_COMPILE_OPTION_DENORMAL_MODE_F64, VKD3D_SHADER_DENORMAL_MODE_ANY},
     };
 
     stage_desc->sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
diff --git a/libs/vkd3d/libs/vkd3d/utils.c b/libs/vkd3d/libs/vkd3d/utils.c
index 2d0510e5456a..a86572b11005 100644
--- a/libs/vkd3d/libs/vkd3d/utils.c
+++ b/libs/vkd3d/libs/vkd3d/utils.c
@@ -454,6 +454,14 @@ const struct vkd3d_format *vkd3d_get_format(const struct d3d12_device *device,
             return &vkd3d_formats[i];
     }
 
+    /* Combined depth/stencil formats can be used without
+     * D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL as well. In some ways it may be
+     * nicer to add entries for these to vkd3d_formats[], much like we do with
+     * e.g. DXGI_FORMAT_R32_TYPELESS, but we can't handle the override for the
+     * VK_FORMAT_D24_UNORM_S8_UINT formats there. */
+    if ((format = vkd3d_get_depth_stencil_format(device, dxgi_format)))
+        return format;
+
     /* Do not check VkPhysicalDevice4444FormatsFeaturesEXT because apps
      * should query format support, which returns more detailed info. */
     if (dxgi_format == format_b4g4r4a4.dxgi_format && device->vk_info.EXT_4444_formats)
diff --git a/loader/wine.inf.in b/loader/wine.inf.in
index aece31f8dc1a..f66d4ead0221 100644
--- a/loader/wine.inf.in
+++ b/loader/wine.inf.in
@@ -325,6 +325,7 @@ HKLM,System\CurrentControlSet\Control\Class\{745a17a0-74d3-11d0-b6fe-00a0c90f57d
 [CurrentVersion]
 HKCU,%CurrentVersion%\Explorer\Advanced,,16
 HKCU,%CurrentVersion%\Explorer\FileExts,,16
+HKCU,%CurrentVersion%\ImmersiveShell,"TabletMode",0x10003,0
 HKCU,%CurrentVersion%\Run,,16
 HKCU,%CurrentVersionNT%\Winlogon,,16
 HKLM,%CurrentVersion%,"CommonFilesDir",,"%16427%"
diff --git a/nls/sortdefault.nls b/nls/sortdefault.nls
index 9e03193d5a05ca37224a41434763adde67e1d60d..ae19f1c3a6eb117023d236e14a5d41492c2ed0d3 100644
GIT binary patch
delta 18097
zcmeI32YeGpzQ?_@yOP{ilA8cy8+U{YEv5!jOfMlosHTM8t4+W*Ai#hjgis7@LNhgl
z0O5!OLqbAiTe56yOLE8sC9HPy!V4)Rfq;QPir-JzX?O4LUG8$9_dYy-K79S1{qO8(
zXLmF^qgiT>*_oOb9gLv!^77Jo8N8P6F?fAcd|KG6V!{efsHU@we_@c%s08ktPK4y2
z4r*4oCB^LDm;?~Q0uJy%1=K*3KR3+~rbS8*0ze=L0>K~z81fIKCA5x4Di*{6Z=eH>
zKn#cn&4CfL09-&@!WG_6Q!mqz2l!aP0UqS*%pr^_Ki*sz%g1}M1k^dfhmcqO2ubw+
zl(YlgfETOEuQjKc_;_FB^hHiT*bnxH{b2zX-~c!P4uk{YAUFsPhJ)b{I0SA2H-SUp
zP`D{eVgby@hrwZRI2;Z~!O<Y5H~z$eIMCuDJgkaOKso^#6Oc|sIuYqaq?3?NLOKcQ
zWTcakPDWMK#pw*6Ke;#_e>WDlH}Psz3-D@G3-D@G3-D@G3-D@G3-D@G3-D@G3-D@G
z3-D@G3-D@G3-D@G3-D@Gi3c#RMzsL1MzsL1Mzz$amfBM-HL9gXwbZB<;DJhwY5^Xo
z)TkEVfl7^P0UoH-s21RXDnG9zJ&9NQ;3IMRe1Aj-fI;91AoviL!9~EmR^wi)y~r6f
z%HYBn%gZdMLW?ML+8WJo-T|qOpcCi}9tT}OD(DKjf$ktr*Pfxe_M}x`QwBCO!D8?X
zSOS)UERYRyz%sBLtN<pk608ENK`vMW)`E2)53C0pz_VZ@cn)j=&x6fi3)l*_Js?9!
zJ`&r(4zLr5;05p^*adcjJzy``2MWM`Z~z<xhrnTQ1iS=}>Q<7EO+9{0O-%zmK`+o7
z^Z|WAKhPfx00Y4w@B|nPo&-a{P%sQU1%`tWU?dm?MuRaR9gGFzz<4kLJPjuPm}*L$
zgv4Yp1xy9gz;rMJ%mlN*Y%mAR1@pjsumCIsi$Dg*1dG8lU<p_XvOqS-0n5O0umYIC
zO0eojlA2n$H8PhJ?vKp;?Vm{8_<8KPY>f-o>H|QXpRrdWf9~~YkidySOc*^8#nt+H
zoLuitT&=;gJ(TnGjx=ZlC-(Jh@8fDad;DR2zH3}<nrFKOUzabL7j>TPRK8a0@e_Pq
zxnt`0kFVc9uYUiE`u)$=duP25{>5>x)gM<}AGg>0!}@aN`u#s`->Tmx4|{hSq*94~
zp7RM%)tygxy<60~ovQA9d#h^WJ^AL<AGb)w$MMG)FRxd2gmcqVk0W&gMBqts5`Gm#
zAsr1)Asq|Hfq2jy7(ok=0RF_BDdE0g$Z3Y2BHVa}yv9)N9@P-uuu;$j0J2dChr{6z
zID{@apfWOkCZSgVK$OFY9aK$W@^zYS+KobQWa^D@U$}4n1kHb_HwqC*N96bMJ{E5h
zmSdlX6~F{m0#7lkz-qwxZxnL#4L(+s#rYsFFoE2{>ptjkSuZa}MGc2l#~5~_z|)c=
zs=*{bAalI0N#H^@3VvXDeop2lHMX0e2vIwlOJ}(J*^67_08T6z&9(Oq#Rqwa6qV5&
zF0H5@!zJ*e5E+%<{+R<TPBZ_?GjHS6d6c(~7Nm0}Op*>Y))A8s00$s%0IKEj7<ddk
zAD$0SgQwBW$5g9Gp>E|2g5tQsoYi-jP?Mm-v3$S=fkhrQ&;S+qws7N`wJg<5;kJ|f
z!`m96IyGd2a0|uN<lDB5!}d)a_BFEJ%x{_ha2xx-#{QdBoX)8UABKIs#6vT=0f9Lr
zQkaQdd1RR|3+d${DU_MT4PjG8<MT(O4rAaka5|ihf^MVm+i(qBgCp0Xu(j|8c!QX?
zmyE;3SdYMZ@%mnpgpD=WvxY{@<E}EoSTaOd9?(Y65Q~8RQ};b<_S}!a4FU=DT=Xm7
zvx_|e76H#)py_K>9}=2UscNI5(_d5B8U0x_R0__4H^7^;_v@;-;Gai7bW6GF2w~o$
z?J89U4Re7$@t!K$mod{tifSKe=xsf{EyD=KxODjEMF8uyBZ6DRl<e5Xbrmd#wnnfG
zXzN95|5LS%2{$5J3y=U3K@vy?EkP?HZYJEvq%{#USZ+1n2C=sE!KbQ^Gz~+(M`y@q
z{D+5p)VD_Ep7uLNc<E}YOV>P_BfP_!hCU}X^$_|Ts(V>;j{DVM?u16qQRcK}3*+-5
z`ixy*HxZRNJ|W^(!hJ*z)O9G#L3+1PbL;mss8DaJ3-x(48`Kn^h6eRhgPP$}jTSW5
zXA2Xq!GO;j_yAwv2mFBmv~1lIO2<BYLNR)JcfQZ>YEYMbsP3}wquHPudMM8vw62GW
z@hjqlKs1?OkARx{VFa`k!7Pvsa=<dM9IODQ`u4T5u6^;V5L+!iNbpPaR>gm)CWM&%
z^IMvaHw=gVe-4LQ`TefpkmyHsqJKkY^XUHIRoB`4r~hw;L!bFGoUjI6$FKXIFa5VM
zKA$7_1-Q<N$_Z`#=;)jJ&gK?&-2QcE<Fqw&HjmiP?Z0d8<v)?IFIFuO=il`|!tS*D
zQNum|KXYt{${>2<JO8Z=d!eX|=KkQ{nrD-20;NRIYuKoYK(WF{IK!T|CDL49;VZ)C
z+hS;jAao?`ZF2{DN+)#Z5R0a_g9N>b-DDXgszZhQ>^aAH$~G0e88)q?i>M6~BH6{Z
ztF&#H(3r4J+h=regs_%n^UOwiIZC+0v8!x{s3Ts8W!Qr9MDbp8p@<!5?<}4-3ai-6
z3ODWALfGKVer!*s6>WtLgza1;(2?x~D`EYe=fsGPLODCaYN6LV3WHd-rQ-#$a~FK(
zU~?*+&_$@?SnraPv}F%r2hSE%4WhO_LPv&GJ2FJJpRk<$$of9b?I%oS*@e#M>Ggr0
z1NPd6((^-vJ1iS++d&J53n~21lh<-b*xy~Az{FM9LQMDUL1ehKIr*Y8khCvpOx~|-
zPEJ`nkzwWj#A*#7X{C|m-O2>AsjLa<XN@8=%lt_=I9c6{5FC^1XiQ#nv?nS{PcjGj
zQp)ut&N7_jR)vu6&SNB_SVaP>1rk-!kMyWsO<uR8WB+QD@ff*iOA{l;3oo)Rhc7)p
zUbxC&fLrjizz}vrRWv;`Nf@qX9VPwgcQb{?D)zKpPYnx%&j~A*7$}<|@C<vxrlY=@
z!a^4Lx>M5<fnl`z@|Va0OB8vm#GgD@(VlKzDon(ZUXge!TX>i4Sbdb*a)iAs+uC-N
zPB#hTG;C^Rh-k~h?ObWs(T(ec<pTS)r4#iPg_9hcR@zKFzgy_Vb~0yDc8}-C_e)36
zLj}TAp6yb-g!&%AZ>a3yveshYd0`0K!+DiXKQFZAaB|7w?stSY*pI8C#gS$qi%lpu
z&`V~|=@wTGqkSyGRF3_=tQkF3CTwEZt3@fa>qViXntiU)N^`1(L4=)CnM*4!35$7l
zf5~Gs?E_(ufvqWSL-TJ6<r;QINdld4U)aR4Uz=0tjUR-s)NE-{1yy)$mYOwKchYo!
zEv|lsgC;lCUemBoUK}JEVzq18ZRI;?L9BM1KfA!vp4PU}c4XPn)(Nz8JMAYbwoT=e
zVssbn8TKo?FTK)5dq;y0$fTEgYu$t$TXKdL_S1gSf}AX&yE3)mDt2^bvUomQyOotn
z8q>i!+LJs|gJ|ta?eNB|-Wf(m?$TN{?1suNVuXoAK3ov%fFDvfS82(dvOa&cAlB2e
zm%LiM>))~<*515z2>HCKC)rUBusHKaEQnFvVeKWxw4(iwsVh2wj-V6h3?2tvuws-7
zx`J-l?ha%5Yz3CjR$%#T1(wfNVEJstzqWj4`d#a1fBf=UOVsKgUq7pxx*XNLtmQ=A
zXYA8#jx9vY_>3L?t6y3k4lP>Y2i;!{E!dOV=b5NcoR`;VFb1T9v0xk+4<>-8xw@(I
zM6Pb?Jc-^trTtw~XM>LF4EjfNG~4i7%r8^tDf)gKKLt%@Djh#VU*gM5r%{>u8){}I
z4OphHRWq|`z#6?)!_1{o&*{|~GM}RfG2FYX{{kHJJsRf^U?KH+L9g~>GHAeIPv*rm
z>V*C~b-)tj!9On^EJY%Vqb*~&k6Efer?1g6IaF1q_vM7;*!A004YGpUk<az;F=5Y2
zu!`HEaI@&#Dt!zSRe?Yyr~-E208UU1-UXL9ilvQd4F4WB{!AY@^zZsUe1{>=c|}j1
zR{|Cce)Qf-pt{7s-%d=}?yTxEt^ngD8X9pId|XI>0I)p7rUarF>4Mvm3c7-BpgZUR
z(m+qpi?6?qy|K}UK4>5KuKMA1q|UB^jb{Hm+WMbg200!`b;pAq#omPKYJz@y+5K0s
zq#rL1i^1Ale_Hx|(6g#P^sTN0yt$PKl0Y)}Z{O;o_u1g)95awc>4Nbke!}BZ8sSOY
z$X`E98V92pypG@*a28N-4!i;01n0q9eBHz3ZT{iIM0EkNcX;u;1iys(AIci$u8-WS
zVhwZGfA8E?{1@h~dBOju=C02MW9}M23!V%9dvn)4!9UGi3xa=|yY3JE-<`Ww2mhV9
z>-FH@&RxYzUj}cWV~d2oL_B;Wc>g~;i{(R>2U7Ewf|LE*b6I1^PjlJUA%8WO?Hkft
zBWA{E_y5I&c1XxSJ)vD3vNnQtn5li*_m7&^4rrLxHcV^(#<Z60hBx6{A<klN!mGRn
zU~a&O2M!q;iT!pPe5v1V!x@!dp=Une<@H`;ywW5V?KT98A0068%o*|80Yfx%Ui3K#
zy)7m~e((H}x1G4^Ao4Cah`dGOGoDP3iMu>HBwq034Z!d2_2qVYn0MzPWX%;-hmfa6
zY~j&l@d?Op8pg`?SyLaLA2UNl?r3I~_@+m5MA?&V-Y?mD)SYRw!zgl*7<U+%GR6KL
zJtNM8{IY+^+xuaWe#?G|^r`!h_r(uANAwkc^c*n&gRT0L=wDZQw<9Qhkl6bOG7YA)
zju?FP#*Gg%{rty%P2<(gRjVBQi};A(ynSa4pb=;cnt)Ky6oi3r5CNKjNDu|0>Atfu
z%NFrdC4;3gH5g-ed-FZyZB=pfRGZjg3|}DME=v<@+s0nuOXZ_QarE+Iv4?zkZ};HR
zx9O6uvFR*-#9dt^Qb&(iR>KR5%F&!U`p5b<=G!U3rOoK&$+4k9d}rlP){FGUlGu$Z
z{(b35+XcF0MeHSCK3aLj*^2ggHukDNf7!kM;!Ya>LTtJ>e?iGE52B_6v3RTffikHa
zpKFiJ*YG3UTWr2``sLWP2!5D+%-)F}y&HQzhCk%?E?G_|gvRBB^Xrty%ch8zJH|!x
zTb0jBqv*YkaruOgk=m7>qIr+UrN#17l;h_2sBL0gp&x(Teb1q%OXkHTaQxfyQ1b{{
zuqZCnpSQR-R5ll>DeemYqdU}Tpqp35T_wCjzHdvX^K(7fZz<!dFH$it&ey>EO80G@
z@(T9F(Sm*Pmk#j5<qoB1^5%DIPUm+shVn_Pl@E%}lIgBorMhfCyhhpSsDRfho$L|t
zI%Qk=88}ZFY5krS^fKn?gVW^G_Czwn)l)7lS`GJt4~P*XjiF)v<+0Z82n~>Htq<UV
z^4f|i@F2OBeXiJNq%qoms65I%8=<G<{EN5f^pVDm8h!)r&vW#~1mk3bX|FP~<Q$pl
zaw`|CrDT??t+cu51l(5|Xbyn~OJ6t-!LLe<%9_J&=@n;7_&e0JEo^X~vZcUkd7wQO
z_K`bR$)318yi^az%Pq>9!mZ?06@%b*^2?Pz@F=;^v5m|!x%SGD&Y=iwR6-ojli98%
z(mHc*_y_kkrwM*TzEhq6JLSH`QE(q+PwA`hc4dZRHhd73NrGQeG-dtC9M?q2WQ&K_
zOBE$2;1{JaRSGOg7fYMKp>Clx8jf(=D+dwn99N9{K(Qwf>;A-U6!VrC6HKjSL&XGy
zy32a=A-Gbmtm+3l<++x*uooKgX1KZ1t}F~5t<*RM!4s4(9HZe$%7k)He6rHS-Ugnp
z{AgbeFHk<Q_l6fK;f_Xdj^ef#!ON8lhaEnyoGHFe=9pY3lthc?fC^=JX=B)_Y;$%a
zb6uP?&1r;H(kG=+u(#y2O@ejOw(^CrUOHlF0tZTWDpKGO=_@phxvoZ1Zs{_3qI9+_
z9zH95YPkb{Dm^Ia1%EE}Elz-MNT;lKiKtm>Y~i2l-eT>7*mn0&^K7x@Qe$Y!NA7!0
zJwn&qnxeVzr|!{J!{E=|?aE@{>+V6;V)$!!wlxF3=^jxsN*s(sgTv)1_D%>z%U@eO
zXB;E@nj4GLm*Q$C$@(e-LhI$(j%s*=+}7%Vx5>37$?$f0y>%?SLw2C)%ysRQdla{W
zUzESPI1oM}e`Oy5pOoLWdCIvWr&Mo;WqDNTDfnZ#qC5h=Di3fTfWMNzDQyeimWc)B
zC%f*-wxTu&d@En6DuSCUY34*YQTdbe8@RplX=NDPL789;6i=Z#{5{2FixX`){VuUm
ztxZqnx#FbUsz7*%RO@I8Z<D^Z-G$FdXNvp4H{8jN5wJ~ORWca9CZ8!jFV-$KM#j&>
z4gbQroXmF-X<o?%I8^H4XbTT@541l8k8<xfUl8N7jFBenR0vkG=DWg__ltD!Jmsz8
zFtWg9l#bYA;1-g~5e>JOwp2EOpOyrN4L&AWt$pB&Qjh94V58gH`98ecJ*oUC{Hpt6
z=_L5i?oIYc*yT>OzYKriUT1Cw-*=Z=Mr)9Wm&aCSil!`MG(TFo;oK<}WEn$EO(YqO
zZ=vfc>B;I;c%-CQcf%7UcTszIy>z6aBfMRjY#9xUQj)C?{DSnjV>x_X%B+|Ue=iNR
z4}*KSZ`#7)KzXwx7!Hv?w1&Wq<kyOW;HGkovl$#FM^`^a77DINxz;fQfhhT2X&3QE
zmN6kIMSdEKbtntn%~-g%e4=D6++PkTFNFulK8~~SxAK|t0<mSbF~R>?sa@4pvdCqY
zUa?OSM`jz_#_HTXEtv>~xi?$m;8^#X>Va@8_e^U~PgZwIMXb0PM|Vk3@-Jp1l%%x4
zH*JwCSuqtE;MR&z`Z(N1*>}+hcTj37$G{zxH!6eRPD(eNvyyG>%uiO-4nZ{J7^8z1
zDY;d<ur~u8$LnyW^1bzx*fqx(I`y!k#dmCx>$Fnqcn5w*iMAxcX62N16Kqv(+GoRd
zWuUzY$#Ah!V!6j7q^rf<;R$FgC*f(*gNrJ7k94xg4&Rb8@c_th-Ik7(G#2x6a6&p%
zbA<R#j&T^z%h!q`MC~$sZClIhDqcaxWpc9PWq7@O6DN}4%9lScnnyCx_hi_fheO?0
z(6kr3db@M0^l+fu&)OP(Nw!!Hl4o2t>6_y5a4UCr=TUf_yrB9%S>oz1b+R^t$I9(+
zC6~H3yOV5%@I+Zt7DtwvT$AMP)oT%$BJZ?Bz|-XdTPt{mya|i7OI@?%;_4K5wydwz
z!*k@U;^FXoIke~;$#ON9bk1EcaT7cdvRu5|$AbT}=JIhD*!sb%;GXblcXP)SxWDY}
zJOK}o(@I_;rYzizWtPqeELOIc4272}iMX^`t}G?f+7!-K`dWfWwkupptmp~fl3H3)
z;oDMM>jt<+npYGD-;s7!_kr(9y0X{cds5HRGPsTVxH%K9aj!0F0^e~Tv5$q*<>Tgk
zCL|8Y=c=c}N8|}5o#A5ibd%tw%D0YdaG3IzWgQ%@G%HGkBa{=?1h|=^E31Jcm9Y;`
zBuaVH-VNK)O09hd9HWF)dp>BYGSl(`JWbhOv=5%4oO85<XDRwBQv?zVloWjBvt1u3
zLWPm6aHU9V@D*K&8iW?_A*)<h-Pz8junPyaCaYcL?zC!=<hmA0saXBYb&Ydtaiwxy
zneH!(M!+ArYaDmrPuv5GJ=<>iZSx6OmM@sck~OZErBx+q@F^*)P4QYJPUFEg9X>C0
zuGj`YkiM)6gZtvrWx-G38E_I_FMsPur(NS?uP_ax+=fwZ!zj05l-n@MZ5ZV?jB*=B
gxecS-hEeXLG|J7JP@M#lK}*mIw5AiP+w3U$FBiaCkpKVy

delta 8633
zcmeI0d301ozQ_Ak-Rj%vbXRvL>2$&p$kGX&kg$YB76lR@EV3vMgdnmLS!4+?vaiD`
z4oiR%7`6Z=j3P_KLUA-?A%ui<0GB99H%A9l90x@h4=`iiw@C-Y_h!y}{_*A=hn#%+
z`>U#7Ro!3Ts=B}1H@CT!n+GctZq?~@)~Zn5oLLUt<?ToLPQB77?{$3!RYLPZj6)Mt
zrK5pl9Sv!kx1nvmSyh4v$r_j%79Ie@2m*l#m;ndYtfi?9m9VUxsR@ZOC_8}xSbzgW
zgIEv;ngJJ}LCJ*X-A>gOY_)2QvKr+Z6_yp3pXa1;I!ZvDCKw5M!$e4P^B1HY=n8a{
zW!2`VE~asT@C<~f32K6xp=KzDa%d1V2pS9xhK4{xpmwMo8VU`CHh?yOHiR~WHloA{
zpfs*Av@tXS8Uc-hMuV81_;7+)5PuH}RpSz1C%`cQc5~RxVK;}J2s;sWBJ3pCNwAZs
znxz#CS7_GQf;fD>RM37g)esh-8o~lpLs)=n2n$dRVF9WkEI>7c1*nFw0M!r{pc=vg
zR6|&RY6yt|DAf=apc=vgR6|%A!qR+UX$VV0SQ^3t6sQ`)0u-nk!U7bi8o~k;s2aio
z6sTFN3Wq0B&4?*+`K%9W`+|O;Kj20}73#asM8mz-bmTY|oINk5D3@mChp2g#C1KOD
zHbypEWo@rWYkShN7fO{m)+OY#yp55|N#35wSr30i5^0@EPs}r5b>I7wm0!|Jk@uIj
z4B@H>Q5wnDqtx0!pC<XWqgw0mm7i99TR?^kQAICbd6!z-$)}(C{jRFDslIYNtMf}@
z!s;tO!D=l&?a%7QP4~Ay=Wn0tZ(rnZU+dSce$D<_zhnM>1^)U9zkcc;*W+)mFW>N&
z$-ONsL-cxK@?B4mzV3P={2K4qcKW*O?WwPg^Z8Bn_si6?SVr*O9o!P3P9}d)<V)k&
zo2u-xQy-~jjbcxWV|(>~F)iY$L4d5~1f8?>;}l`-%D9Zk)n_Yt`R0Kn&pKxrk(-X`
z7olmL>Qr8r?aKAFn(Ylmr#lRefTQ4;?D>H{)_(uMksH3!A0V=AxxTetX57$URg|}6
z|C{>gK;?wYtkv%#xcLg<@1QUKb-s(Gx-CuAOy%U3O=_2<cTw94m90TC_#?{if%ic^
zI0XtoAt+L$SJgHtVk1#66BiLPsk)5CW6T73N7F7F9-T$Mp0mjHKR%0OV2D;T0jCYl
zp81-z&NtMZJ<>XgQbhz88~(m;qwlaeV>qGy<gh6>*wlzbtezy$0<;9JKx>c;+7Rij
zG}N!RZFTES*)IQj(Qv~*U#9?R?PPp-z7<CQe5;M;)rj9?pq*eB*bVl8y&xOx!vyP$
z^%EHAy#=iV$iJ>P)=%(*QCfcpe0YNU0d+T>Qj-Gd!#l`?vKeq-0ajoGK_Hk)Z!c5*
zgmda9WOLCnPu%HiY97GiKGg^z`R1$^`L8|NPXC_UX`rcoJ4utYO7lNheJX!wI~lm5
zCN02H`ss1L5amT+F<1hYf@NSi_$?NriPta2uj&?~{u)~D=)~Q=rUd_LtSou78h>Sp
z=Ktz&=c5T-&$fwaq5J@hLg{eu4^_H>-IdoH@{J1J$i~R!jd?3ZH?gBqMsb^gZf5U@
zvUq+R#+Pz=0{@24E$me}Gl_Q~bSrBw54YsqR4P~>`F$I1)6-Yjd7*XUx9K((Bxz?J
zpuid|ERXX@7|+S%$9Xt`bwv*7!dFnbot4UuQ}`{F{+<O&cN%vpum+1Cdht^*R*I9o
z`BE4;vP&PnCV=i{R#`TPuOYDV<TFEfF`;`{hG;U3m%`XCzaGYsZ0}{$MW+#%b1$1K
zM~>j<RGQ5W%NAq!7Do58a9NVUJ1DT87jz<D2xGNeK9P^7^Z*+tzn<dj@G3?-IfLJ#
zGzV7RY~Ggno{o4j@(pBhXI4QvGdH<b9!%O7hLexVn~}rC9m$|lGwMGCAAyfS75Icq
zJYyyipfjr`1bvpf!^tssdz3GeDdh>IZK;jK77a%EPv8o;3ceswWxYwt`DNtIqTy&?
zhB4ZZvn8pb$s)duKF&hr$wmBgMG29+mvDv9C-AlJTgC@#v<tf?@2=tDdfJW6mW~bl
zD?(FPYe};>Q((=Kft&dZN>kY&xma*Tv81xiWO`8)X;Ww>sZ7ZAuVBrP?o8a+&M(mJ
zEJK#;;5#Yp!Jd*CyZHzMO=B;Jk{sNrG`35=l*<=#+L!f|fs!9m>62`hIC+xyfU#22
zcYQtkvFUQ(`+Pj3{aLvTEaohf{s&7J!I$_`Fs8|jOT3jz2eU<DhnK$%<B)jfGM@{h
zTwc8FyWAn{HJSD&K3=8Iuo8LrOa8J#hq6T3<r?px(P8XIIsZG}kI;0sS(bgzXEQpS
z-IS@fc|Qjo%|4J>R!gaYj$>vyGTidAO2@NgdA+ga8;xeb@;WVZH98Sr>+nR&c}}J<
znbg^G)j+2)n{f27tbma$clWT2Fw;!7QPw_f=|JgB?3_+RELZe&Hft)PM_7)-=r1pg
zu-r1x7ug1Rae}3W&^g#8c@r&H;>mI*w{NyY=;?3SesOZUWh0DIIbet75Th$u2U)wv
zGB}*BV>{$C?^ucrqS0a!<NNhO_gE1p>(5!plrxVJEXMr%$(f~6d_I~c`ZBZ}>luqG
ztbfy=kNPv70^fkYfa~B}@Ey1Tz6Uq)?5MQ-&i^VfRISULh9Nbx)MrketVqpl4?nve
zq-M6>DjTlqbkEA?*4YXJmFHyCE4J&JGD-&RvDIqI7#Wmnvlx_dGU|v;Gmr_YOo&l0
zP;&-4{S5*A9Xv0M@7gqzGD!xV@i|YCQI)p4TF_MZ;9tkUG#JxW*&;^$j7nRz?WRS^
zl=?s00#!Z>O?a*O$x9KLt!~LuQ;fes%Zp%+y2Yzbikb^^9+(dnfQ4WYSPYhcrK(K(
z&K9dkycmsBSQ)Cy<(=<s7Xp2+=Bp}eZL+d9X!-zr@s-j#F!-V8_LsC9)mNa4q7#lV
z^}QrF>`LH!9Xk)b04{<Lz=z-?@G+<YpQzG1H28P*Ge3@qpOANk2Vc;9Gt0`c!C{l@
zGIMvZw7Ns;Z%3H@p=&VUs3cvP7!-pS!*23)Q~O%|FZ&rQ9zRtjfaV|({I@@2$=h-E
zW~!1ZquSVUuY36Pu%UGtuKuUVOD~jrgFc`ycoOsj{lNe*kV$W6d;KDOligc{`r~MM
zl)(JTe<^#E!2C-RnAhz8uM(Iy>~#su5&J(&VDjzt2~45AK7lE+|7#PNTlRmFz*s^b
zPGH1EYv>weGS}ruTj&+?OEVc)=)zz+ig{$A$#RbV<D8~nXnjsIIP|~dG*d!*8fe?I
zq2k5vmYqLKZf1x6;^byq=!zz^QK40KT4Naz_<xY*9Je|u3j!F`>-yp`tBAd2jxf=?
z&=Dw2g^uHT(`&xO0MFcT{2V1t6*_{&<x&Sz4vDL!j%X!M7|%kEizJ9iKJaZPmY#+0
z^t14NTl~i7)Ld-$$qw<J&)10L$v<w#d%j!C;5uLE%i#01i1$gl=npZC!1LYj`oz8K
zQ$`ASM=PVnJ3bjJJU+Ma58P7fuC!@6hMXv3%i%Ox^zq3HVk*Qm<AHC_dqbLLKB!5n
zo8+AM)Yqen_|exR1y57|Mf9l~z3VxQ-d*%O2dAEL(m6+<&9(NP)BTT%&kVY*Rkhyi
zpVbdBuiE8tfG`ja8i0nN5oin|KoiguM1m*~Eq8fh7G%n(r<|_@$bxj|Kt(io)>$R*
zJnP(Jl-U{1p)`j~b{eE{rqd8E8?1ANg~*P3oU8Tn?5oboK$(5adD$%2<~xT5$ZMs}
zp^38356%q+`B|;AYZDpYB=$s%yp|d}Cqhgb6Wdg78WX#L$oTQGU7a#>ZR`<~j2E%Z
zRGGdbHq0#M<;GUY^n<aNiA;IjSDkqz*5FvRtRz-0D~YSzyDEKRGnqco6~^q#z30#D
zAsJOGyd5i=Kv#M<l^&P7r@H3bSa;9qQ_Dn)C9a0%zMk6RJLLJQCp|05#)*t2u4uV^
ziEFh%#;<XWa@cFU?-idWldA4k-!4gpI%*D=v=!I)xWcu`RoPy>J5h+eu7rL%?~&s5
zWJ=XoZ-a`~&}rTuD;7dCy%Fv(=tA#IcLjN&O0Awy>4I9TH<iwS23OxIYYQD+{nnW{
z=oi&@3VR5d?MiO@M$NbGHK;mVqc2(r&8wMQ+1lq)Gwj?T=*^mIm3yI$JVz_Spp894
zPfrjp?sFwH-r{kW8&S2@lTy$QddTy~5(o4f&v&Pj#en^;h9YyntC{&k_3?r<GPSD2
zv$Sx4_;|l7Qkz!QusX$^ESel}MYf+_<*GhV5ksa|#aHXy(a?j{#l>mRv(+i*--doz
z^KwNb^pl#E`Awm>Yf6iTiIM}ZXmR6!E3ED6>H}pR$c(DZ)nki>K}B_9Ng6bz=G&48
zXs~C!+YSx$94iQcMtW-9&kOOYE5V-P$#Xx8sy?2e($mns9;5p$aUD$}KF8IPHLh7-
z94i**pnNRH6=r_Sd$T;9WL9<XzFi(7zQ}QP67jjNrfjD7O2wOEWG;eUmg@?$S9qVS
zXh3FG4XOUTpc`}~LOdkAx$uuV=o%>I9dtFbFYqL}bJ4ik^R4@RXqM-zQ&VM^0nSUx
eqrLEGFFe`{t40PSffk@8Xa!o!kpanD3jY(-dIsbG

diff --git a/programs/regedit/framewnd.c b/programs/regedit/framewnd.c
index c2af414d2689..3e2bbce3f2b0 100644
--- a/programs/regedit/framewnd.c
+++ b/programs/regedit/framewnd.c
@@ -36,7 +36,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(regedit);
  */
 
 static const WCHAR favoritesKey[] =  L"Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit\\Favorites";
-static BOOL bInMenuLoop = FALSE;        /* Tells us if we are in the menu loop */
 static WCHAR favoriteName[128];
 static WCHAR searchString[128];
 static int searchMask = SEARCH_KEYS | SEARCH_VALUES | SEARCH_CONTENT;
@@ -81,13 +80,11 @@ static void OnEnterMenuLoop(HWND hWnd)
     /* Update the status bar pane sizes */
     nParts = -1;
     SendMessageW(hStatusBar, SB_SETPARTS, 1, (LPARAM)&nParts);
-    bInMenuLoop = TRUE;
     SendMessageW(hStatusBar, SB_SETTEXTW, 0, (LPARAM)&empty);
 }
 
 static void OnExitMenuLoop(HWND hWnd)
 {
-    bInMenuLoop = FALSE;
     /* Update the status bar pane sizes*/
     SetupStatusBar(hWnd, TRUE);
     UpdateStatusBar();
diff --git a/programs/uninstaller/main.c b/programs/uninstaller/main.c
index 8a22cc7288c9..e053bc2346c6 100644
--- a/programs/uninstaller/main.c
+++ b/programs/uninstaller/main.c
@@ -43,7 +43,6 @@ typedef struct {
 } uninst_entry;
 static uninst_entry *entries = NULL;
 static unsigned int numentries = 0;
-static int oldsel = -1;
 static WCHAR *sFilter;
 static BOOL silent;
 
@@ -281,7 +280,6 @@ static int FetchUninstallInformation(void)
     HKEY root;
 
     numentries = 0;
-    oldsel = -1;
     if (!entries)
         entries = malloc(sizeof(uninst_entry));
 
diff --git a/server/class.c b/server/class.c
index 8598c231a03f..2ca94bca7bee 100644
--- a/server/class.c
+++ b/server/class.c
@@ -52,8 +52,7 @@ struct window_class
 
 C_ASSERT( sizeof(class_shm_t) == offsetof(class_shm_t, extra[0]) );
 
-static struct window_class *create_class( struct process *process, int local, struct unicode_str *name, unsigned int name_offset,
-                                          atom_t atom, mod_handle_t instance, unsigned int style, int cls_extra, int win_extra )
+static struct window_class *create_class( struct process *process, int local, int cls_extra )
 {
     struct window_class *class;
 
@@ -64,19 +63,6 @@ static struct window_class *create_class( struct process *process, int local, st
     class->local = local;
 
     if (!(class->shared = alloc_shared_object( offsetof(class_shm_t, extra[cls_extra]) ))) goto failed;
-    SHARED_WRITE_BEGIN( class->shared, class_shm_t )
-    {
-        memcpy( (void *)shared->name, name->str, name->len );
-        shared->name_offset  = name_offset;
-        shared->name_len     = name->len;
-        shared->atom         = atom;
-        shared->instance     = instance;
-        shared->style        = style;
-        shared->win_extra    = win_extra;
-        shared->cls_extra    = cls_extra;
-        memset( (void *)shared->extra, 0, cls_extra );
-    }
-    SHARED_WRITE_END;
 
     /* other fields are initialized by caller */
 
@@ -95,7 +81,7 @@ static void destroy_class( struct window_class *class )
     struct atom_table *table = get_user_atom_table();
 
     release_atom( table, class->atom );
-    release_atom( table, class->shared->atom );
+    release_atom( table, class->shared->info.atom );
     list_remove( &class->entry );
     release_object( class->process );
     if (class->shared) free_shared_object( class->shared );
@@ -122,9 +108,9 @@ static struct window_class *find_class( struct process *process, atom_t atom, mo
     {
         const class_shm_t *shared = class->shared;
         if (class->atom != atom) continue;
-        is_win16 = !(shared->instance >> 16);
-        if (!instance || !class->local || shared->instance == instance ||
-            (!is_win16 && ((shared->instance & ~0xffff) == (instance & ~0xffff)))) return class;
+        is_win16 = !(shared->info.instance >> 16);
+        if (!instance || !class->local || shared->info.instance == instance ||
+            (!is_win16 && ((shared->info.instance & ~0xffff) == (instance & ~0xffff)))) return class;
     }
     return NULL;
 }
@@ -136,7 +122,7 @@ struct window_class *grab_class( struct process *process, atom_t atom, mod_handl
     if (class)
     {
         class->count++;
-        *extra_bytes = class->shared->win_extra;
+        *extra_bytes = class->shared->info.win_extra;
         *locator = get_shared_object_locator( class->shared );
     }
     else set_error( STATUS_INVALID_HANDLE );
@@ -151,7 +137,7 @@ void release_class( struct window_class *class )
 
 int is_desktop_class( struct window_class *class )
 {
-    return (class->shared->atom == DESKTOP_ATOM && !class->local);
+    return (class->shared->info.atom == DESKTOP_ATOM && !class->local);
 }
 
 int is_message_class( struct window_class *class )
@@ -160,17 +146,17 @@ int is_message_class( struct window_class *class )
     static const struct unicode_str name = { messageW, sizeof(messageW) };
     struct atom_table *table = get_user_atom_table();
 
-    return (!class->local && class->shared->atom == find_atom( table, &name ));
+    return (!class->local && class->shared->info.atom == find_atom( table, &name ));
 }
 
 int get_class_style( struct window_class *class )
 {
-    return class->shared->style;
+    return class->shared->info.style;
 }
 
 atom_t get_class_atom( struct window_class *class )
 {
-    return class->shared->atom;
+    return class->shared->info.atom;
 }
 
 client_ptr_t get_class_client_ptr( struct window_class *class )
@@ -195,10 +181,16 @@ DECL_HANDLER(create_class)
     struct window_class *class;
     struct unicode_str name = get_req_unicode_str();
     struct atom_table *table = get_user_atom_table();
-    atom_t atom = req->atom, base_atom;
-    unsigned int offset = 0;
+    unsigned int name_offset = 0;
+    atom_t atom = req->atom;
+    struct class_info info;
     WCHAR buffer[16];
 
+    if (name.len < sizeof(info)) return set_error( STATUS_INVALID_PARAMETER );
+    memcpy( &info, name.str, sizeof(info) );
+    name.str += sizeof(info) / sizeof(WCHAR);
+    name.len -= sizeof(info);
+
     if (atom && !name.len) name = integral_atom_name( buffer, atom );
     if (!atom && !(atom = add_atom( table, &name ))) return;
 
@@ -206,11 +198,11 @@ DECL_HANDLER(create_class)
     {
         struct unicode_str base = name;
 
-        offset = req->name_offset;
-        base.str += offset;
-        base.len -= offset * sizeof(WCHAR);
+        name_offset = req->name_offset;
+        base.str += name_offset;
+        base.len -= name_offset * sizeof(WCHAR);
 
-        if (!(base_atom = add_atom( table, &base )))
+        if (!(info.atom = add_atom( table, &base )))
         {
             release_atom( table, atom );
             return;
@@ -218,37 +210,47 @@ DECL_HANDLER(create_class)
     }
     else
     {
-        base_atom = grab_atom( table, atom );
+        info.atom = grab_atom( table, atom );
     }
 
-    class = find_class( current->process, atom, req->instance );
+    class = find_class( current->process, atom, info.instance );
     if (class && !class->local == !req->local)
     {
         set_win32_error( ERROR_CLASS_ALREADY_EXISTS );
         release_atom( table, atom );
-        release_atom( table, base_atom );
+        release_atom( table, info.atom );
         return;
     }
-    if (req->cls_extra < 0 || req->cls_extra > 4096 || req->win_extra < 0 || req->win_extra > 4096)
+    if (info.cls_extra > 4096 || info.win_extra > 4096)
     {
         /* don't allow stupid values here */
         set_error( STATUS_INVALID_PARAMETER );
         release_atom( table, atom );
-        release_atom( table, base_atom );
+        release_atom( table, info.atom );
         return;
     }
 
-    if (!(class = create_class( current->process, req->local, &name, offset, base_atom,
-                                req->instance, req->style, req->cls_extra, req->win_extra )))
+    if (!(class = create_class( current->process, req->local, info.cls_extra )))
     {
         release_atom( table, atom );
-        release_atom( table, base_atom );
+        release_atom( table, info.atom );
         return;
     }
     class->atom       = atom;
     class->client_ptr = req->client_ptr;
+
+    SHARED_WRITE_BEGIN( class->shared, class_shm_t )
+    {
+        memcpy( (void *)shared->name, name.str, name.len );
+        shared->name_offset  = name_offset;
+        shared->name_len     = name.len;
+        memcpy( (void *)&shared->info, &info, sizeof(info) );
+        memset( (void *)shared->extra, 0, info.cls_extra );
+    }
+    SHARED_WRITE_END;
+
     reply->locator   = get_shared_object_locator( class->shared );
-    reply->atom      = base_atom;
+    reply->atom      = info.atom;
 }
 
 /* destroy a window class */
@@ -291,8 +293,8 @@ DECL_HANDLER(set_class_info)
         switch (req->offset)
         {
         case GCL_STYLE:
-            reply->old_info = shared->style;
-            shared->style = req->new_info;
+            reply->old_info = shared->info.style;
+            shared->info.style = req->new_info;
             break;
         case GCL_CBWNDEXTRA:
             if (req->new_info > 4096)
@@ -300,19 +302,43 @@ DECL_HANDLER(set_class_info)
                 set_error( STATUS_INVALID_PARAMETER );
                 return;
             }
-            reply->old_info = shared->win_extra;
-            shared->win_extra = req->new_info;
+            reply->old_info = shared->info.win_extra;
+            shared->info.win_extra = req->new_info;
             break;
         case GCL_CBCLSEXTRA:
             set_win32_error( ERROR_INVALID_INDEX );
             break;
         case GCLP_HMODULE:
-            reply->old_info = shared->instance;
-            shared->instance = req->new_info;
+            reply->old_info = shared->info.instance;
+            shared->info.instance = req->new_info;
+            break;
+        case GCLP_WNDPROC:
+            reply->old_info = shared->info.wndproc;
+            shared->info.wndproc = req->new_info;
+            break;
+        case GCLP_HCURSOR:
+            reply->old_info = shared->info.cursor;
+            shared->info.cursor = req->new_info;
+            break;
+        case GCLP_HBRBACKGROUND:
+            reply->old_info = shared->info.background;
+            shared->info.background = req->new_info;
+            break;
+        case GCLP_HICON:
+            reply->old_info = shared->info.icon;
+            shared->info.icon = req->new_info;
+            break;
+        case GCLP_HICONSM:
+            reply->old_info = shared->info.icon_small;
+            shared->info.icon_small = req->new_info;
+            break;
+        case GCLP_MENUNAME:
+            reply->old_info = shared->info.menu_name;
+            shared->info.menu_name = req->new_info;
             break;
         default:
             if (req->size > sizeof(req->new_info) || req->offset < 0 ||
-                req->offset > class->shared->cls_extra - (int)req->size)
+                req->offset > class->shared->info.cls_extra - (int)req->size)
             {
                 set_win32_error( ERROR_INVALID_INDEX );
                 return;
diff --git a/server/protocol.def b/server/protocol.def
index 3ee3be605d4e..4f4f90c80e1f 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1038,17 +1038,28 @@ typedef volatile struct
     unsigned __int64     keystate_serial;  /* keystate update counter at last sync */
 } input_shm_t;
 
-typedef volatile struct
+struct class_info
 {
     atom_t               atom;               /* class atom */
     unsigned int         style;              /* class style */
     unsigned int         cls_extra;          /* number of class extra bytes */
     unsigned int         win_extra;          /* number of window extra bytes */
+    user_handle_t        cursor;             /* default cursor */
+    user_handle_t        background;         /* default background */
+    user_handle_t        icon;               /* default icon */
+    user_handle_t        icon_small;         /* default cursor (small) */
     mod_handle_t         instance;           /* module instance */
+    client_ptr_t         wndproc;            /* class window proc */
+    client_ptr_t         menu_name;          /* pointer to menu name in client address space */
+};
+
+typedef volatile struct
+{
     data_size_t          name_offset;        /* offset in WCHAR of the unversioned class name, constant */
     data_size_t          name_len;           /* len in bytes of the class name, constant */
     WCHAR                name[MAX_ATOM_LEN]; /* class name, constant */
     unsigned short       __pad;
+    struct class_info    info;               /* class info (GCLP_*) */
     char                 extra[];            /* extra bytes storage */
 } class_shm_t;
 
@@ -1056,6 +1067,8 @@ typedef volatile struct
 {
     struct obj_locator   class;            /* object locator for the window class shared object */
     unsigned int         dpi_context;      /* DPI awareness context */
+    unsigned int         fnid;             /* builtin class FNID, or 0 */
+    data_size_t          private_size;     /* length of private extra bytes range */
 } window_shm_t;
 
 typedef volatile union
@@ -2682,6 +2695,14 @@ enum message_type
 @END
 
 
+/* Set the window builtin class FNID */
+@REQ(set_window_fnid)
+    user_handle_t  handle;        /* handle to the window */
+    atom_t         atom;          /* class atom */
+    unsigned int   fnid;          /* builtin class FNID */
+@END
+
+
 /* Set the parent of a window */
 @REQ(set_parent)
     user_handle_t  handle;      /* handle to the window */
@@ -3268,12 +3289,9 @@ enum caret_state
 @REQ(create_class)
     int            local;          /* is it a local class? */
     atom_t         atom;           /* class atom */
-    unsigned int   style;          /* class style */
-    mod_handle_t   instance;       /* module instance */
     client_ptr_t   client_ptr;     /* pointer to class in client address space */
-    short int      cls_extra;      /* number of extra class bytes */
-    short int      win_extra;      /* number of window extra bytes */
     data_size_t    name_offset;    /* base class name offset for specified atom */
+    VARARG(info,class_info);       /* class info */
     VARARG(name,unicode_str);      /* class name */
 @REPLY
     struct obj_locator locator;    /* locator for the shared class object */
@@ -3288,6 +3306,8 @@ enum caret_state
     VARARG(name,unicode_str);      /* class name */
 @REPLY
     client_ptr_t   client_ptr;     /* pointer to class in client address space */
+    user_handle_t  background;     /* class default background */
+    client_ptr_t   menu_name;      /* pointer to menu name in client address space */
 @END
 
 
diff --git a/server/request_handlers.h b/server/request_handlers.h
index cf80212dbc5f..0d7a66ef38ea 100644
--- a/server/request_handlers.h
+++ b/server/request_handlers.h
@@ -156,6 +156,7 @@ DECL_HANDLER(set_window_owner);
 DECL_HANDLER(get_window_info);
 DECL_HANDLER(init_window_info);
 DECL_HANDLER(set_window_info);
+DECL_HANDLER(set_window_fnid);
 DECL_HANDLER(set_parent);
 DECL_HANDLER(get_window_parents);
 DECL_HANDLER(get_window_list);
@@ -466,6 +467,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_get_window_info,
     (req_handler)req_init_window_info,
     (req_handler)req_set_window_info,
+    (req_handler)req_set_window_fnid,
     (req_handler)req_set_parent,
     (req_handler)req_get_window_parents,
     (req_handler)req_get_window_list,
@@ -644,6 +646,7 @@ C_ASSERT( sizeof(object_id_t) == 8 );
 C_ASSERT( sizeof(process_id_t) == 4 );
 C_ASSERT( sizeof(short int) == 2 );
 C_ASSERT( sizeof(struct async_data) == 40 );
+C_ASSERT( sizeof(struct class_info) == 56 );
 C_ASSERT( sizeof(struct context_data) == 1720 );
 C_ASSERT( sizeof(struct cursor_pos) == 24 );
 C_ASSERT( sizeof(struct filesystem_event) == 12 );
@@ -1526,6 +1529,10 @@ C_ASSERT( offsetof(struct set_window_info_request, new_info) == 24 );
 C_ASSERT( sizeof(struct set_window_info_request) == 32 );
 C_ASSERT( offsetof(struct set_window_info_reply, old_info) == 8 );
 C_ASSERT( sizeof(struct set_window_info_reply) == 16 );
+C_ASSERT( offsetof(struct set_window_fnid_request, handle) == 12 );
+C_ASSERT( offsetof(struct set_window_fnid_request, atom) == 16 );
+C_ASSERT( offsetof(struct set_window_fnid_request, fnid) == 20 );
+C_ASSERT( sizeof(struct set_window_fnid_request) == 24 );
 C_ASSERT( offsetof(struct set_parent_request, handle) == 12 );
 C_ASSERT( offsetof(struct set_parent_request, parent) == 16 );
 C_ASSERT( sizeof(struct set_parent_request) == 24 );
@@ -1840,13 +1847,9 @@ C_ASSERT( offsetof(struct get_hook_info_reply, unicode) == 32 );
 C_ASSERT( sizeof(struct get_hook_info_reply) == 40 );
 C_ASSERT( offsetof(struct create_class_request, local) == 12 );
 C_ASSERT( offsetof(struct create_class_request, atom) == 16 );
-C_ASSERT( offsetof(struct create_class_request, style) == 20 );
-C_ASSERT( offsetof(struct create_class_request, instance) == 24 );
-C_ASSERT( offsetof(struct create_class_request, client_ptr) == 32 );
-C_ASSERT( offsetof(struct create_class_request, cls_extra) == 40 );
-C_ASSERT( offsetof(struct create_class_request, win_extra) == 42 );
-C_ASSERT( offsetof(struct create_class_request, name_offset) == 44 );
-C_ASSERT( sizeof(struct create_class_request) == 48 );
+C_ASSERT( offsetof(struct create_class_request, client_ptr) == 24 );
+C_ASSERT( offsetof(struct create_class_request, name_offset) == 32 );
+C_ASSERT( sizeof(struct create_class_request) == 40 );
 C_ASSERT( offsetof(struct create_class_reply, locator) == 8 );
 C_ASSERT( offsetof(struct create_class_reply, atom) == 24 );
 C_ASSERT( sizeof(struct create_class_reply) == 32 );
@@ -1854,7 +1857,9 @@ C_ASSERT( offsetof(struct destroy_class_request, atom) == 12 );
 C_ASSERT( offsetof(struct destroy_class_request, instance) == 16 );
 C_ASSERT( sizeof(struct destroy_class_request) == 24 );
 C_ASSERT( offsetof(struct destroy_class_reply, client_ptr) == 8 );
-C_ASSERT( sizeof(struct destroy_class_reply) == 16 );
+C_ASSERT( offsetof(struct destroy_class_reply, background) == 16 );
+C_ASSERT( offsetof(struct destroy_class_reply, menu_name) == 24 );
+C_ASSERT( sizeof(struct destroy_class_reply) == 32 );
 C_ASSERT( offsetof(struct set_class_info_request, window) == 12 );
 C_ASSERT( offsetof(struct set_class_info_request, offset) == 16 );
 C_ASSERT( offsetof(struct set_class_info_request, size) == 20 );
diff --git a/server/request_trace.h b/server/request_trace.h
index 4b0ddc179b9b..65c7530e05a9 100644
--- a/server/request_trace.h
+++ b/server/request_trace.h
@@ -21,6 +21,7 @@ static void dump_varargs_acl( const char *prefix, data_size_t size );
 static void dump_varargs_apc_call( const char *prefix, data_size_t size );
 static void dump_varargs_apc_result( const char *prefix, data_size_t size );
 static void dump_varargs_bytes( const char *prefix, data_size_t size );
+static void dump_varargs_class_info( const char *prefix, data_size_t size );
 static void dump_varargs_contexts( const char *prefix, data_size_t size );
 static void dump_varargs_cursor_positions( const char *prefix, data_size_t size );
 static void dump_varargs_debug_event( const char *prefix, data_size_t size );
@@ -1767,6 +1768,13 @@ static void dump_set_window_info_reply( const struct set_window_info_reply *req
     dump_uint64( " old_info=", &req->old_info );
 }
 
+static void dump_set_window_fnid_request( const struct set_window_fnid_request *req )
+{
+    fprintf( stderr, " handle=%08x", req->handle );
+    fprintf( stderr, ", atom=%04x", req->atom );
+    fprintf( stderr, ", fnid=%08x", req->fnid );
+}
+
 static void dump_set_parent_request( const struct set_parent_request *req )
 {
     fprintf( stderr, " handle=%08x", req->handle );
@@ -2395,12 +2403,9 @@ static void dump_create_class_request( const struct create_class_request *req )
 {
     fprintf( stderr, " local=%d", req->local );
     fprintf( stderr, ", atom=%04x", req->atom );
-    fprintf( stderr, ", style=%08x", req->style );
-    dump_uint64( ", instance=", &req->instance );
     dump_uint64( ", client_ptr=", &req->client_ptr );
-    fprintf( stderr, ", cls_extra=%d", req->cls_extra );
-    fprintf( stderr, ", win_extra=%d", req->win_extra );
     fprintf( stderr, ", name_offset=%u", req->name_offset );
+    dump_varargs_class_info( ", info=", cur_size );
     dump_varargs_unicode_str( ", name=", cur_size );
 }
 
@@ -2420,6 +2425,8 @@ static void dump_destroy_class_request( const struct destroy_class_request *req
 static void dump_destroy_class_reply( const struct destroy_class_reply *req )
 {
     dump_uint64( " client_ptr=", &req->client_ptr );
+    fprintf( stderr, ", background=%08x", req->background );
+    dump_uint64( ", menu_name=", &req->menu_name );
 }
 
 static void dump_set_class_info_request( const struct set_class_info_request *req )
@@ -3667,6 +3674,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] =
     (dump_func)dump_get_window_info_request,
     (dump_func)dump_init_window_info_request,
     (dump_func)dump_set_window_info_request,
+    (dump_func)dump_set_window_fnid_request,
     (dump_func)dump_set_parent_request,
     (dump_func)dump_get_window_parents_request,
     (dump_func)dump_get_window_list_request,
@@ -3977,6 +3985,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
     (dump_func)dump_get_window_info_reply,
     NULL,
     (dump_func)dump_set_window_info_reply,
+    NULL,
     (dump_func)dump_set_parent_reply,
     (dump_func)dump_get_window_parents_reply,
     (dump_func)dump_get_window_list_reply,
@@ -4287,6 +4296,7 @@ static const char * const req_names[REQ_NB_REQUESTS] =
     "get_window_info",
     "init_window_info",
     "set_window_info",
+    "set_window_fnid",
     "set_parent",
     "get_window_parents",
     "get_window_list",
diff --git a/server/trace.c b/server/trace.c
index c9812d818bdd..b02e08fce03c 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1654,6 +1654,20 @@ static void dump_varargs_monitor_infos( const char *prefix, data_size_t size )
     remove_data( size );
 }
 
+static void dump_varargs_class_info( const char *prefix, data_size_t size )
+{
+    const struct class_info *info = cur_data;
+
+    fprintf( stderr, "%s{atom=%#x,style=%#x,cls_extra=%u,win_extra=%u,cursor=%#x,background=%#x,icon=%#x,icon_small=%#x",
+             prefix, info->atom, info->style, info->cls_extra, info->win_extra, info->cursor,
+             info->background, info->icon, info->icon_small );
+    dump_uint64( ",instance=", &info->instance );
+    dump_uint64( ",wndproc=", &info->wndproc );
+    dump_uint64( ",menu_name=", &info->menu_name );
+    fputc( '}', stderr );
+    remove_data( sizeof(*info) );
+}
+
 void trace_request(void)
 {
     enum request req = current->req.request_header.req;
diff --git a/server/window.c b/server/window.c
index f222b5c2a590..3fa71fc3f1c3 100644
--- a/server/window.c
+++ b/server/window.c
@@ -92,8 +92,6 @@ struct window
     int              prop_inuse;      /* number of in-use window properties */
     int              prop_alloc;      /* number of allocated window properties */
     struct property *properties;      /* window properties array */
-    int              private_off;     /* offset of private extra bytes range */
-    int              private_len;     /* length of private extra bytes range */
     int              nb_extra_bytes;  /* number of extra bytes */
     char            *extra_bytes;     /* extra bytes storage */
     window_shm_t    *shared;          /* window in session shared memory */
@@ -689,8 +687,10 @@ static struct window *create_window( struct window *parent, struct window *owner
     if (!(win->shared = alloc_shared_object( sizeof(*win->shared) ))) goto failed;
     SHARED_WRITE_BEGIN( win->shared, window_shm_t )
     {
-        shared->class       = class_locator;
-        shared->dpi_context = NTUSER_DPI_PER_MONITOR_AWARE;
+        shared->class           = class_locator;
+        shared->dpi_context     = NTUSER_DPI_PER_MONITOR_AWARE;
+        shared->fnid            = 0;
+        shared->private_size    = 0;
     }
     SHARED_WRITE_END;
 
@@ -2275,9 +2275,27 @@ DECL_HANDLER(create_window)
     reply->class_ptr   = get_class_client_ptr( win->class );
 }
 
-static BOOL in_private_data_range( const struct window *win, int offset, int size )
+
+/* Set the window builtin class FNID */
+DECL_HANDLER(set_window_fnid)
 {
-    return offset < win->private_off + win->private_len && offset + size >= win->private_off;
+    struct obj_locator class_locator;
+    struct window_class *class;
+    struct window *win;
+    int extra_bytes;
+
+    if (!(win = get_window( req->handle ))) return;
+    if (is_desktop_window( win ) && win->thread != current) return set_error( STATUS_ACCESS_DENIED );
+    if (win->shared->fnid && win->shared->fnid != req->fnid) return set_error( STATUS_INVALID_PARAMETER );
+
+    if (!(class = grab_class( current->process, req->atom, 0, &extra_bytes, &class_locator ))) return;
+    SHARED_WRITE_BEGIN( win->shared, window_shm_t )
+    {
+        shared->fnid            = req->fnid;
+        shared->private_size    = extra_bytes;
+    }
+    SHARED_WRITE_END;
+    release_class( class );
 }
 
 
@@ -2404,7 +2422,7 @@ DECL_HANDLER(get_window_info)
     default:
         if (req->size > sizeof(reply->info) || req->offset < 0 ||
             req->offset > win->nb_extra_bytes - (int)req->size ||
-            in_private_data_range( win, req->offset, req->size ))
+            req->offset < win->shared->private_size)
         {
             set_win32_error( ERROR_INVALID_INDEX );
             break;
@@ -2471,10 +2489,6 @@ DECL_HANDLER(set_window_info)
         reply->old_info = win->user_data;
         win->user_data = req->new_info;
         break;
-    case GWLP_FNID_INTERNAL:
-        win->private_off = FNID_OFF(req->new_info);
-        win->private_len = FNID_LEN(req->new_info);
-        break;
     default:
         if (req->size > sizeof(req->new_info) || req->offset < 0 ||
             req->offset > win->nb_extra_bytes - (int)req->size)
diff --git a/tools/make_requests b/tools/make_requests
index 17e425537b55..7e317647a517 100755
--- a/tools/make_requests
+++ b/tools/make_requests
@@ -68,6 +68,7 @@ my %formats =
     "struct thread_info"       => [ 40, 8 ],
     "union udp_endpoint"       => [ 32, 4 ],
     "struct user_apc"          => [ 40, 8 ],
+    "struct class_info"        => [ 56, 8 ],
 );
 
 my $file_header =
diff --git a/tools/make_unicode b/tools/make_unicode
index 2da184f23992..6dda5573c982 100755
--- a/tools/make_unicode
+++ b/tools/make_unicode
@@ -43,8 +43,8 @@ my %data_files =
                 sha  => "fa6d331e52d59771009c424bd1c9df311b6b2bf0bf2f6c1ddf6c952c32da4702" },
  cldr33    => { url  => "https://www.unicode.org/Public/cldr/33/cldr-common-33.0.zip",
                 sha  => "fa3490082c086d21257153609642f54fcf788fcfda4966fe67f3f6daca0d58b9" },
- sorting   => { url  => "https://download.microsoft.com/download/C/F/7/CF713A5E-9FBC-4FD6-9246-275F65C0E498/Windows 10 Sorting Weight Table.txt",
-                sha  => "81fcfa1e5ed3e3a94d329959ff7d97d522ddf9d653d2c4d6ddcccc5cd4df663f" },
+ sorting   => { url  => "https://download.microsoft.com/download/C/F/7/CF713A5E-9FBC-4FD6-9246-275F65C0E498/Windows 11 Sorting Weight Table.txt",
+                sha  => "f0bab873ed8901ae17afc4848c7d9e0a7de4310942effb2b54b538411b26d542" },
  codepages => { url  => "https://download.microsoft.com/download/C/F/7/CF713A5E-9FBC-4FD6-9246-275F65C0E498/Windows Supported Code Page Data Files.zip",
                 sha  => "5074e6dd253056ba61fc6c870c9a955467855129c6ad3a51761c386b301b125a" },
  iso639    => { url  => "https://iso639-3.sil.org/sites/iso639-3/files/downloads/iso-639-3_Code_Tables_$ISO639VERSION.zip",
