From af785463a8e9fc8ff47ead4143151e9831cbbddf Mon Sep 17 00:00:00 2001
From: Jetro Jormalainen <jje-wine@jv.jetro.fi>
Date: Tue, 30 Apr 2019 09:21:24 +1000
Subject: [PATCH] dinput: Support username in Config dialog.

---
 dlls/dinput/config.c         | 183 ++++++++++++++++++++++++++++++-------------
 dlls/dinput/device.c         |   2 +-
 dlls/dinput/device_private.h |   1 +
 dlls/dinput/dinput_main.c    |  27 +++++++
 4 files changed, 156 insertions(+), 57 deletions(-)

diff --git a/dlls/dinput/config.c b/dlls/dinput/config.c
index bf44898..cd2c4b9 100644
--- a/dlls/dinput/config.c
+++ b/dlls/dinput/config.c
@@ -29,6 +29,9 @@ typedef struct {
     IDirectInputDevice8W *lpdid;
     DIDEVICEINSTANCEW ddi;
     DIDEVICEOBJECTINSTANCEW ddo[256];
+    /* ActionFormat for every user.
+     * In same order as ConfigureDevicesData usernames */
+    DIACTIONFORMATW *user_afs;
 } DeviceData;
 
 typedef struct {
@@ -38,10 +41,11 @@ typedef struct {
 
 typedef struct {
     IDirectInput8W *lpDI;
-    LPDIACTIONFORMATW lpdiaf;
     LPDIACTIONFORMATW original_lpdiaf;
     DIDevicesData devices_data;
     int display_only;
+    int nusernames;
+    WCHAR **usernames;
 } ConfigureDevicesData;
 
 /*
@@ -57,27 +61,42 @@ static BOOL CALLBACK collect_objects(LPCDIDEVICEOBJECTINSTANCEW lpddo, LPVOID pv
     return DIENUM_CONTINUE;
 }
 
-static BOOL CALLBACK count_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
+static BOOL CALLBACK collect_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
 {
-    DIDevicesData *data = (DIDevicesData*) pvRef;
+    ConfigureDevicesData *data = (ConfigureDevicesData*) pvRef;
+    DeviceData *device;
+    int i, j;
 
-    data->ndevices++;
-    return DIENUM_CONTINUE;
-}
+    IDirectInputDevice_AddRef(lpdid);
 
-static BOOL CALLBACK collect_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
-{
-    DIDevicesData *data = (DIDevicesData*) pvRef;
-    DeviceData *device = &data->devices[data->ndevices];
+    /* alloc array for devices if this is our first device */
+    if (!data->devices_data.ndevices)
+        data->devices_data.devices = HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceData) * (dwRemaining + 1));
+    device = &data->devices_data.devices[data->devices_data.ndevices];
     device->lpdid = lpdid;
     device->ddi = *lpddi;
 
-    IDirectInputDevice_AddRef(lpdid);
-
     device->nobjects = 0;
     IDirectInputDevice_EnumObjects(lpdid, collect_objects, (LPVOID) device, DIDFT_ALL);
 
-    data->ndevices++;
+    device->user_afs = HeapAlloc(GetProcessHeap(), 0, sizeof(*device->user_afs)*data->nusernames);
+    memset(device->user_afs, 0, sizeof(*device->user_afs)*data->nusernames);
+    for (i = 0; i < data->nusernames; i++)
+    {
+        DIACTIONFORMATW *user_af = &device->user_afs[i];
+        user_af->dwNumActions = data->original_lpdiaf->dwNumActions;
+        user_af->guidActionMap = data->original_lpdiaf->guidActionMap;
+        user_af->rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*data->original_lpdiaf->dwNumActions);
+        memset(user_af->rgoAction, 0, sizeof(DIACTIONW)*data->original_lpdiaf->dwNumActions);
+        for (j = 0; j < user_af->dwNumActions; j++)
+        {
+            user_af->rgoAction[j].dwSemantic = data->original_lpdiaf->rgoAction[j].dwSemantic;
+            user_af->rgoAction[j].u.lptszActionName = data->original_lpdiaf->rgoAction[j].u.lptszActionName;
+        }
+        IDirectInputDevice8_BuildActionMap(lpdid, user_af, data->usernames[i], 0);
+    }
+
+    data->devices_data.ndevices++;
     return DIENUM_CONTINUE;
 }
 
@@ -170,10 +189,18 @@ static DeviceData* get_cur_device(HWND dialog)
     return &data->devices_data.devices[sel];
 }
 
-static LPDIACTIONFORMATW get_cur_lpdiaf(HWND dialog)
+static DIACTIONFORMATW *get_cur_lpdiaf(HWND dialog)
+{
+    ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
+    int controller_sel = SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_GETCURSEL, 0, 0);
+    int player_sel = SendDlgItemMessageW(dialog, IDC_PLAYERCOMBO, CB_GETCURSEL, 0, 0);
+    return &data->devices_data.devices[controller_sel].user_afs[player_sel];
+}
+
+static DIACTIONFORMATW *get_original_lpdiaf(HWND dialog)
 {
     ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
-    return data->lpdiaf;
+    return data->original_lpdiaf;
 }
 
 static int dialog_display_only(HWND dialog)
@@ -182,40 +209,36 @@ static int dialog_display_only(HWND dialog)
     return data->display_only;
 }
 
-static void init_devices(HWND dialog, IDirectInput8W *lpDI, DIDevicesData *data, LPDIACTIONFORMATW lpdiaf)
+static void init_devices(HWND dialog, ConfigureDevicesData *data)
 {
     int i;
 
-    /* Count devices */
-    data->ndevices = 0;
-    IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, count_devices, (LPVOID) data, 0);
-
-    /* Allocate devices */
-    data->devices = HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceData) * data->ndevices);
-
     /* Collect and insert */
-    data->ndevices = 0;
-    IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, collect_devices, (LPVOID) data, 0);
+    data->devices_data.ndevices = 0;
+    IDirectInput8_EnumDevicesBySemantics(data->lpDI, NULL, data->original_lpdiaf, collect_devices, (LPVOID) data, 0);
 
-    for (i=0; i < data->ndevices; i++)
-        SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->devices[i].ddi.tszProductName );
+    for (i = 0; i < data->devices_data.ndevices; i++)
+        SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->devices_data.devices[i].ddi.tszProductName );
+    for (i = 0; i < data->nusernames; i++)
+        SendDlgItemMessageW(dialog, IDC_PLAYERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->usernames[i]);
 }
 
 static void destroy_data(HWND dialog)
 {
-    int i;
+    int i, j;
     ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
     DIDevicesData *devices_data = &data->devices_data;
 
     /* Free the devices */
     for (i=0; i < devices_data->ndevices; i++)
+    {
         IDirectInputDevice8_Release(devices_data->devices[i].lpdid);
+        for (j=0; j < data->nusernames; j++)
+            HeapFree(GetProcessHeap(), 0, devices_data->devices[i].user_afs[j].rgoAction);
+        HeapFree(GetProcessHeap(), 0, devices_data->devices[i].user_afs);
+    }
 
     HeapFree(GetProcessHeap(), 0, devices_data->devices);
-
-    /* Free the backup LPDIACTIONFORMATW  */
-    HeapFree(GetProcessHeap(), 0, data->original_lpdiaf->rgoAction);
-    HeapFree(GetProcessHeap(), 0, data->original_lpdiaf);
 }
 
 static void fill_device_object_list(HWND dialog)
@@ -231,6 +254,7 @@ static void fill_device_object_list(HWND dialog)
     /* Add each object */
     for (i=0; i < device->nobjects; i++)
     {
+        DWORD ddo_inst, ddo_type;
         int action = -1;
 
         item.mask = LVIF_TEXT | LVIF_PARAM;
@@ -241,12 +265,20 @@ static void fill_device_object_list(HWND dialog)
 
         /* Add the item */
         SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTITEMW, 0, (LPARAM) &item);
+        ddo_inst = DIDFT_GETINSTANCE(device->ddo[i].dwType);
+        ddo_type = DIDFT_GETTYPE(device->ddo[i].dwType);
 
-        /* Search for an assigned action  for this device */
+        /* Search for an assigned action for this device */
         for (j=0; j < lpdiaf->dwNumActions; j++)
         {
+            DWORD af_inst = DIDFT_GETINSTANCE(lpdiaf->rgoAction[j].dwObjID);
+            DWORD af_type = DIDFT_GETTYPE(lpdiaf->rgoAction[j].dwObjID);
+            if (af_type == DIDFT_PSHBUTTON) af_type = DIDFT_BUTTON;
+            if (af_type == DIDFT_RELAXIS) af_type = DIDFT_AXIS;
+            /* NOTE previously compared dwType == dwObjId but default buildActionMap actions
+             * were PSHBUTTON and RELAXS and didnt show up on config */
             if (IsEqualGUID(&lpdiaf->rgoAction[j].guidInstance, &device->ddi.guidInstance) &&
-                lpdiaf->rgoAction[j].dwObjID == device->ddo[i].dwType)
+                ddo_inst == af_inst && ddo_type & af_type)
             {
                 action = j;
                 break;
@@ -260,7 +292,7 @@ static void fill_device_object_list(HWND dialog)
 static void show_suitable_actions(HWND dialog)
 {
     DeviceData *device = get_cur_device(dialog);
-    LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
+    LPDIACTIONFORMATW lpdiaf = get_original_lpdiaf(dialog);
     int i, added = 0;
     int obj = lv_get_cur_item(dialog);
 
@@ -329,24 +361,35 @@ static void assign_action(HWND dialog)
     lv_set_action(dialog, obj, action, lpdiaf);
 }
 
-static void copy_actions(LPDIACTIONFORMATW to, LPDIACTIONFORMATW from)
+static void reset_actions(HWND dialog)
 {
-    DWORD i;
-    for (i=0; i < from->dwNumActions; i++)
+    ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
+    DIDevicesData *ddata = (DIDevicesData*) &data->devices_data;
+    unsigned i, j;
+
+    for (i = 0; i < data->devices_data.ndevices; i++)
     {
-        to->rgoAction[i].guidInstance = from->rgoAction[i].guidInstance;
-        to->rgoAction[i].dwObjID = from->rgoAction[i].dwObjID;
-        to->rgoAction[i].dwHow = from->rgoAction[i].dwHow;
-        to->rgoAction[i].u.lptszActionName = from->rgoAction[i].u.lptszActionName;
+        DeviceData *device = &ddata->devices[i];
+        for (j = 0; j < data->nusernames; j++)
+            IDirectInputDevice8_BuildActionMap(device->lpdid, &device->user_afs[j], data->usernames[j], DIDBAM_HWDEFAULTS);
     }
 }
 
-static void reset_actions(HWND dialog)
-{
+static void save_actions(HWND dialog) {
     ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
-    LPDIACTIONFORMATW to = data->lpdiaf, from = data->original_lpdiaf;
-
-    copy_actions(to, from);
+    DIDevicesData *ddata = (DIDevicesData*) &data->devices_data;
+    unsigned i, j;
+    if (!data->display_only) {
+        for (i = 0; i < ddata->ndevices; i++)
+        {
+            DeviceData *device = &ddata->devices[i];
+            for (j = 0; j < data->nusernames; j++)
+            {
+                if (save_mapping_settings(device->lpdid, &device->user_afs[j], data->usernames[j]) != DI_OK)
+                    MessageBoxA(dialog, "Could not save settings", 0, MB_ICONERROR);
+            }
+        }
+    }
 }
 
 static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
@@ -358,21 +401,16 @@ static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM w
             ConfigureDevicesData *data = (ConfigureDevicesData*) lParam;
 
             /* Initialize action format and enumerate devices */
-            init_devices(dialog, data->lpDI, &data->devices_data, data->lpdiaf);
+            init_devices(dialog, data);
 
             /* Store information in the window */
             SetWindowLongPtrW(dialog, DWLP_USER, (LONG_PTR) data);
 
             init_listview_columns(dialog);
 
-            /* Create a backup action format for CANCEL and RESET operations */
-            data->original_lpdiaf = HeapAlloc(GetProcessHeap(), 0, sizeof(*data->original_lpdiaf));
-            data->original_lpdiaf->dwNumActions = data->lpdiaf->dwNumActions;
-            data->original_lpdiaf->rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*data->lpdiaf->dwNumActions);
-            copy_actions(data->original_lpdiaf, data->lpdiaf);
-
             /* Select the first device and show its actions */
             SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_SETCURSEL, 0, 0);
+            SendDlgItemMessageW(dialog, IDC_PLAYERCOMBO, CB_SETCURSEL, 0, 0);
             fill_device_object_list(dialog);
 
             ShowCursor(TRUE);
@@ -414,6 +452,7 @@ static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM w
                     break;
 
                 case IDC_CONTROLLERCOMBO:
+                case IDC_PLAYERCOMBO:
 
                     switch (HIWORD(wParam))
                     {
@@ -424,12 +463,12 @@ static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM w
                     break;
 
                 case IDOK:
+                    save_actions(dialog);
                     EndDialog(dialog, 0);
                     destroy_data(dialog);
                     break;
 
                 case IDCANCEL:
-                    reset_actions(dialog);
                     EndDialog(dialog, 0);
                     destroy_data(dialog);
                     break;
@@ -452,15 +491,47 @@ HRESULT _configure_devices(IDirectInput8W *iface,
                            LPVOID pvRefData
 )
 {
+    int i;
+    DWORD default_username_size = MAX_PATH;
+    WCHAR *default_username = 0;
     ConfigureDevicesData data;
     data.lpDI = iface;
-    data.lpdiaf = lpdiCDParams->lprgFormats;
+    data.original_lpdiaf = lpdiCDParams->lprgFormats;
     data.display_only = !(dwFlags & DICD_EDIT);
+    data.nusernames = lpdiCDParams->dwcUsers;
+    if (lpdiCDParams->lptszUserNames == NULL)
+    {
+        /* Get default user name */
+        default_username = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*MAX_PATH);
+        data.nusernames = 1;
+        data.usernames = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
+        data.usernames[0] = default_username;
+        GetUserNameW(default_username, &default_username_size);
+    }
+    else
+    {
+        WCHAR *p = lpdiCDParams->lptszUserNames;
+        data.usernames = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *)*data.nusernames);
+        for (i = 0; i < data.nusernames; i++)
+        {
+            if (*p)
+            {
+                data.usernames[i] = p;
+                while (*(p++));
+            }
+            else
+                /* Return if there is an empty string */
+                return DIERR_INVALIDPARAM;
+        }
+    }
 
     InitCommonControls();
 
     DialogBoxParamW(DINPUT_instance, (const WCHAR *)MAKEINTRESOURCE(IDD_CONFIGUREDEVICES),
             lpdiCDParams->hwnd, ConfigureDevicesDlgProc, (LPARAM)&data);
 
+    HeapFree(GetProcessHeap(), 0, default_username);
+    HeapFree(GetProcessHeap(), 0, data.usernames);
+
     return DI_OK;
 }
diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c
index b441743..0c94326 100644
--- a/dlls/dinput/device.c
+++ b/dlls/dinput/device.c
@@ -692,7 +692,7 @@ static HKEY get_mapping_key(const WCHAR *device, const WCHAR *username, const WC
     return hkey;
 }
 
-static HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername)
+HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername)
 {
     WCHAR *guid_str = NULL;
     DIDEVICEINSTANCEW didev;
diff --git a/dlls/dinput/device_private.h b/dlls/dinput/device_private.h
index af8d99d..36b71f7 100644
--- a/dlls/dinput/device_private.h
+++ b/dlls/dinput/device_private.h
@@ -123,6 +123,7 @@ extern const char *_dump_dinput_GUID(const GUID *guid)  DECLSPEC_HIDDEN;
 
 extern LPDIOBJECTDATAFORMAT dataformat_to_odf_by_type(LPCDIDATAFORMAT df, int n, DWORD type)   DECLSPEC_HIDDEN;
 
+extern HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername) DECLSPEC_HIDDEN;
 extern BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdiaf, const WCHAR *username) DECLSPEC_HIDDEN;
 
 extern HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, DWORD devMask, LPCDIDATAFORMAT df)  DECLSPEC_HIDDEN;
diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c
index 198c6a8..ee319f0 100644
--- a/dlls/dinput/dinput_main.c
+++ b/dlls/dinput/dinput_main.c
@@ -1251,9 +1251,34 @@ static HRESULT WINAPI IDirectInput8AImpl_ConfigureDevices(
 
     /* Copy parameters */
     diCDParamsW.dwSize = sizeof(DICONFIGUREDEVICESPARAMSW);
+    diCDParamsW.dwcUsers = lpdiCDParams->dwcUsers;
     diCDParamsW.dwcFormats = lpdiCDParams->dwcFormats;
     diCDParamsW.lprgFormats = &diafW;
     diCDParamsW.hwnd = lpdiCDParams->hwnd;
+    diCDParamsW.lptszUserNames = NULL;
+
+    if (lpdiCDParams->lptszUserNames) {
+        char *start = lpdiCDParams->lptszUserNames;
+        WCHAR *to = NULL;
+        int total_len = 0;
+        for (i = 0; i < lpdiCDParams->dwcUsers; i++)
+        {
+            char *end = start + 1;
+            int len;
+            while (*(end++));
+            len = MultiByteToWideChar(CP_ACP, 0, start, end - start, NULL, 0);
+            total_len += len + 2; /* length of string and two null char */
+            if (to)
+                to = HeapReAlloc(GetProcessHeap(), 0, to, sizeof(WCHAR) * total_len);
+            else
+                to = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * total_len);
+
+            MultiByteToWideChar(CP_ACP, 0, start, end - start, to + (total_len - len - 2), len);
+            to[total_len] = 0;
+            to[total_len - 1] = 0;
+        }
+        diCDParamsW.lptszUserNames = to;
+    }
 
     diafW.rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*lpdiCDParams->lprgFormats->dwNumActions);
     _copy_diactionformatAtoW(&diafW, lpdiCDParams->lprgFormats);
@@ -1281,6 +1306,8 @@ static HRESULT WINAPI IDirectInput8AImpl_ConfigureDevices(
 
     HeapFree(GetProcessHeap(), 0, diafW.rgoAction);
 
+    HeapFree(GetProcessHeap(), 0, (void*) diCDParamsW.lptszUserNames);
+
     return hr;
 }
 
-- 
1.9.1

