From 2cc9bb08e38c8191c0ef518a967cff2a24715270 Mon Sep 17 00:00:00 2001 From: Cameron Beeley Date: Fri, 10 Jul 2026 23:53:29 +0100 Subject: [PATCH] Fix shift-modifier Extras lost on save and misapplied on load MapFrom recomputed hasExtrasValue from dcs.shiftExtras but was missing the trailing add into shiftExtrasSerializer that its non-shift sibling has, so ShiftControl/Extras was never written to the profile XML and shift-mode extras silently vanished on every save (the old pre-DTO writer appended the node after the same recompute). MapTo's ShiftControl.Extras block still passed shift=false to UpdateDS4CExtra (unedited from the copied Control.Extras block; every other ShiftControl sub-block passes true), so shift extras from older profiles loaded into the normal extras slot - firing without the shift trigger and clobbering the control's normal extras. Add the missing append, pass true for the shift slot, and cover both directions with regression tests. --- DS4Windows/DS4Control/DTOXml/ProfileDTO.cs | 7 ++- DS4WindowsTests/ProfileTests.cs | 52 ++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/DS4Windows/DS4Control/DTOXml/ProfileDTO.cs b/DS4Windows/DS4Control/DTOXml/ProfileDTO.cs index 89f2a62..e13440a 100644 --- a/DS4Windows/DS4Control/DTOXml/ProfileDTO.cs +++ b/DS4Windows/DS4Control/DTOXml/ProfileDTO.cs @@ -1988,6 +1988,11 @@ public void MapFrom(BackingStore source) } } } + + if (hasExtrasValue) + { + shiftExtrasSerializer.CustomMapExtras.Add(dcs.control, dcs.shiftExtras); + } } if (buttonSerializer.CustomMapButtons.Count > 0) @@ -2634,7 +2639,7 @@ public void MapTo(BackingStore destination) foreach (KeyValuePair pair in ShiftControl.Extras.CustomMapExtras) { destination.UpdateDS4CExtra(deviceIndex, - pair.Key.ToString(), false, pair.Value); + pair.Key.ToString(), true, pair.Value); } } diff --git a/DS4WindowsTests/ProfileTests.cs b/DS4WindowsTests/ProfileTests.cs index 5b39a4d..0e6c327 100644 --- a/DS4WindowsTests/ProfileTests.cs +++ b/DS4WindowsTests/ProfileTests.cs @@ -378,5 +378,57 @@ public void CheckWriteProfile() Assert.AreEqual(true, !string.IsNullOrEmpty(testStr)); Assert.AreEqual(defaultProfileXml, testStr); } + + [TestMethod] + public void CheckShiftExtrasWrittenOnSave() + { + // Shift-modifier extras stored in the BackingStore must survive MapFrom + // into the DTO's ShiftControl.Extras so they get serialized. + string shiftExtras = "1,255,0,0,255,0,0,1,0"; + BackingStore tempStore = new BackingStore(); + tempStore.UpdateDS4CExtra(0, "Cross", true, shiftExtras); + + ProfileDTO dto = new ProfileDTO(); + dto.DeviceIndex = 0; + dto.MapFrom(tempStore); + + Assert.IsNotNull(dto.ShiftControl.Extras); + Assert.IsTrue(dto.ShiftControl.Extras.CustomMapExtras.ContainsKey(DS4Controls.Cross)); + Assert.AreEqual(shiftExtras, dto.ShiftControl.Extras.CustomMapExtras[DS4Controls.Cross]); + } + + [TestMethod] + public void CheckShiftExtrasReadIntoShiftSlot() + { + // ShiftControl/Extras in the profile XML must load into the control's + // shift extras slot without clobbering its normal extras. + string normalExtras = "1,255,0,0,255,0,0,1,0"; + string shiftExtras = "1,0,255,0,255,0,0,1,0"; + string profileXml = $@" + + + + {normalExtras} + + + + + {shiftExtras} + + +"; + + XmlSerializer serializer = new XmlSerializer(typeof(ProfileDTO), + ProfileDTO.GetAttributeOverrides()); + using StringReader sr = new StringReader(profileXml); + ProfileDTO dto = serializer.Deserialize(sr) as ProfileDTO; + dto.DeviceIndex = 0; + BackingStore tempStore = new BackingStore(); + dto.MapTo(tempStore); + + DS4ControlSettings dcs = tempStore.GetDS4CSetting(0, "Cross"); + Assert.AreEqual(normalExtras, dcs.extras); + Assert.AreEqual(shiftExtras, dcs.shiftExtras); + } } }