Some games expect the DualSense audio device (used for haptic feedback and controller speaker) to be called “Wireless Controller”.
This is the case of Final Fantasy XIV Online and Final Fantasy VII Remake Intergrade, and possibly other games as well. Together with !337, this is enough for all DualSense features to properly work with those games (provided the controller is plugged in before the game is started).
Note that this is not sufficient for many other games which have more elaborate logic for finding the DualSense audio output.
-- v2: mmdevapi: Override product strings for DualSense controller audio devices
From: Claire Girka claire@sitedethib.com
Some games expect the DualSense audio device (used for haptic feedback and controller speaker) to be called “Wireless Controller”. --- dlls/mmdevapi/devenum.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/dlls/mmdevapi/devenum.c b/dlls/mmdevapi/devenum.c index 053275cac45..43f3db56077 100644 --- a/dlls/mmdevapi/devenum.c +++ b/dlls/mmdevapi/devenum.c @@ -255,6 +255,30 @@ static HRESULT set_driver_prop_value(GUID *id, const EDataFlow flow, const PROPE return hr; }
+struct product_name_overrides +{ + const WCHAR *id; + const WCHAR *product; +}; + +static const struct product_name_overrides product_name_overrides[] = +{ + /* Sony controllers */ + { .id = L"VID_054C&PID_0CE6", .product = L"Wireless Controller" }, +}; + +static const WCHAR *find_product_name_override(const WCHAR *device_id) +{ + const WCHAR *match_id = wcschr( device_id, '\' ) + 1; + DWORD i; + + for (i = 0; i < ARRAY_SIZE(product_name_overrides); ++i) + if (!wcsnicmp( product_name_overrides[i].id, match_id, 17 )) + return product_name_overrides[i].product; + + return NULL; +} + /* Creates or updates the state of a device * If GUID is null, a random guid will be assigned * and the device will be created @@ -321,6 +345,21 @@ static MMDevice *MMDevice_Create(WCHAR *name, GUID *id, EDataFlow flow, DWORD st
pv.vt = VT_LPWSTR; pv.pwszVal = name; + + if (SUCCEEDED(set_driver_prop_value(id, flow, &devicepath_key))) { + PROPVARIANT pv2; + + PropVariantInit(&pv2); + + if (SUCCEEDED(MMDevice_GetPropValue(id, flow, &devicepath_key, &pv2)) && pv2.vt == VT_LPWSTR) { + WCHAR *override; + if ((override = find_product_name_override(pv2.pwszVal)) != NULL) + pv.pwszVal = find_product_name_override(pv2.pwszVal); + } + + PropVariantClear(&pv2); + } + MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv); MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_DeviceInterface_FriendlyName, &pv); MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_DeviceDesc, &pv); @@ -328,8 +367,6 @@ static MMDevice *MMDevice_Create(WCHAR *name, GUID *id, EDataFlow flow, DWORD st pv.pwszVal = guidstr; MMDevice_SetPropValue(id, flow, &deviceinterface_key, &pv);
- set_driver_prop_value(id, flow, &devicepath_key); - if (FAILED(set_driver_prop_value(id, flow, &PKEY_AudioEndpoint_FormFactor))) { pv.vt = VT_UI4;
Andrew Eikum (@aeikum) commented about dlls/mmdevapi/devenum.c:
pv.vt = VT_LPWSTR; pv.pwszVal = name;
if (SUCCEEDED(set_driver_prop_value(id, flow, &devicepath_key))) {
PROPVARIANT pv2;
PropVariantInit(&pv2);
if (SUCCEEDED(MMDevice_GetPropValue(id, flow, &devicepath_key, &pv2)) && pv2.vt == VT_LPWSTR) {
This isn't a blocker, but it feels a little silly to get the value in `set_driver_prop_value` and then get it again here. You could change the `set_driver_prop_value` API to (optionally?) pass back the value it gets to the caller, who would then free it.
Andrew Eikum (@aeikum) commented about dlls/mmdevapi/devenum.c:
pv.vt = VT_LPWSTR; pv.pwszVal = name;
if (SUCCEEDED(set_driver_prop_value(id, flow, &devicepath_key))) {
PROPVARIANT pv2;
PropVariantInit(&pv2);
if (SUCCEEDED(MMDevice_GetPropValue(id, flow, &devicepath_key, &pv2)) && pv2.vt == VT_LPWSTR) {
WCHAR *override;
if ((override = find_product_name_override(pv2.pwszVal)) != NULL)
pv.pwszVal = find_product_name_override(pv2.pwszVal);
Can you just set to `override` here instead of calling `find_product_name_override` twice?
Thanks, this is looking good. Just a couple more nitpicks.
Also this introduces some warnings:
``` /home/aeikum/src/wine/dlls/mmdevapi/devenum.c: In function ‘MMDevice_Create’: /home/aeikum/src/wine/dlls/mmdevapi/devenum.c:356:35: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 356 | if ((override = find_product_name_override(pv2.pwszVal)) != NULL) | ^ /home/aeikum/src/wine/dlls/mmdevapi/devenum.c:357:36: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 357 | pv.pwszVal = find_product_name_override(pv2.pwszVal); | ^ ```
On Thu Jun 30 13:27:07 2022 +0000, Andrew Eikum wrote:
Can you just set to `override` here instead of calling `find_product_name_override` twice?
Sure, that was my intent!
On Thu Jun 30 13:27:07 2022 +0000, Andrew Eikum wrote:
This isn't a blocker, but it feels a little silly to get the value in `set_driver_prop_value` and then get it again here. You could change the `set_driver_prop_value` API to (optionally?) pass back the value it gets to the caller, who would then free it.
Hm no matter what I try it feels a bit awkward.
My C is a bit rusty, so I'm unsure what the best way to fix that is. Should the string be cloned into a mutable one? Functionally, this shouldn't be necessary.
On Thu Jun 30 13:56:10 2022 +0000, Claire wrote:
My C is a bit rusty, so I'm unsure what the best way to fix that is. Should the string be cloned into a mutable one? Functionally, this shouldn't be necessary.
Just change the type of `override` to `const WCHAR *`, I think.
On Thu Jun 30 13:56:10 2022 +0000, Andrew Eikum wrote:
Just change the type of `override` to `const WCHAR *`, I think.
That moves the warning to the next line. Would an explicit cast be ok?
On Thu Jun 30 13:59:16 2022 +0000, Claire wrote:
That moves the warning to the next line. Would an explicit cast be ok?
Oh, because `pwszVal` is non-const. Sure, I think a cast is fine. We never write to it or try to free it.