While working through #37 (mutation-testing the settings/profile serialization code), the surviving mutants kept pointing at unasserted blocks — and reading those blocks, plus a systematic pass over the rest of the DTO serialization code, turned up some real bugs rather than just missing tests. Collecting them here so they're tracked in one place.
1. LastChecked date corrupted across locales — fixed in #42
AppSettingsDTO.LastCheckString writes MM/dd/yyyy but reads back with a culture-dependent DateTime.TryParse, so on a non-US locale the date round-trips with month and day swapped. Fix already open: #42.
2. Legacy per-channel lightbar colour: green/blue read the Red element
In ProfileDTO.PostProcessXml, the legacy <Red>/<Green>/<Blue> path parses RedColorString into all three channels (the green/blue blocks weren't edited after copy-paste), so an old-format profile loads with the lightbar set to (red, red, red). Fix PR opening shortly.
3. Shift-modifier Extras are never saved
In ProfileDTO.MapFrom (~line 1979), the shift copy of the extras block recomputes hasExtrasValue from dcs.shiftExtras but is missing the trailing
if (hasExtrasValue) { shiftExtrasSerializer.CustomMapExtras.Add(dcs.control, dcs.shiftExtras); }
that the non-shift sibling has (~1928-1931). shiftExtrasSerializer is therefore never populated, the Count > 0 guard at ~2044 can never pass, and ShiftControl/Extras is never written to the profile XML — even though MapTo reads it back. Any shift-mode extras a user configures (rumble/lightbar/mouse-sensitivity under a shift trigger) silently vanish on the next save; re-saving an older profile that did contain them strips them permanently. (The old pre-DTO writer in ScpUtil.SaveProfileOld has the recompute followed by the append, which is how the block clearly lost its tail in the port.)
4. Shift-modifier Extras load into the non-shift slot
In ProfileDTO.MapTo (line 2637), the ShiftControl.Extras block calls
destination.UpdateDS4CExtra(deviceIndex, pair.Key.ToString(), false, pair.Value);
with the shift flag still false — unedited from the copied Control.Extras block — while every other ShiftControl sub-block passes true (and the old legacy reader used true for this element). Loading a profile that contains ShiftControl/Extras (e.g. written by an older DS4Windows) applies the shift extras to the control's normal slot: they fire without the shift trigger held, clobber any normal extras on the same control, and the actual shift slot stays empty. Combined with bug 3, one load+save cycle makes the loss permanent.
I'll open a fix PR covering 3+4 together (they're the two halves of the same round-trip) with a regression test for each direction.
Minor / latent (no runtime effect in current builds, noting for completeness)
AppSettingsDTO.ShouldSerializeCustomLed7String uses MAX_DS4_CONTROLLER_COUNT > 7 where its siblings use >= N — currently equivalent because the constant is 8.
AutoProfilesDTO.MapFrom sets Controller4 inside the USING_MAX_CONTROLLERS block although slot 4 is a base slot everywhere else — currently unreachable-false, so no effect.
While working through #37 (mutation-testing the settings/profile serialization code), the surviving mutants kept pointing at unasserted blocks — and reading those blocks, plus a systematic pass over the rest of the DTO serialization code, turned up some real bugs rather than just missing tests. Collecting them here so they're tracked in one place.
1.
LastCheckeddate corrupted across locales — fixed in #42AppSettingsDTO.LastCheckStringwritesMM/dd/yyyybut reads back with a culture-dependentDateTime.TryParse, so on a non-US locale the date round-trips with month and day swapped. Fix already open: #42.2. Legacy per-channel lightbar colour: green/blue read the Red element
In
ProfileDTO.PostProcessXml, the legacy<Red>/<Green>/<Blue>path parsesRedColorStringinto all three channels (the green/blue blocks weren't edited after copy-paste), so an old-format profile loads with the lightbar set to(red, red, red). Fix PR opening shortly.3. Shift-modifier Extras are never saved
In
ProfileDTO.MapFrom(~line 1979), the shift copy of the extras block recomputeshasExtrasValuefromdcs.shiftExtrasbut is missing the trailingthat the non-shift sibling has (~1928-1931).
shiftExtrasSerializeris therefore never populated, theCount > 0guard at ~2044 can never pass, andShiftControl/Extrasis never written to the profile XML — even thoughMapToreads it back. Any shift-mode extras a user configures (rumble/lightbar/mouse-sensitivity under a shift trigger) silently vanish on the next save; re-saving an older profile that did contain them strips them permanently. (The old pre-DTO writer inScpUtil.SaveProfileOldhas the recompute followed by the append, which is how the block clearly lost its tail in the port.)4. Shift-modifier Extras load into the non-shift slot
In
ProfileDTO.MapTo(line 2637), theShiftControl.Extrasblock callswith the shift flag still
false— unedited from the copiedControl.Extrasblock — while every otherShiftControlsub-block passestrue(and the old legacy reader usedtruefor this element). Loading a profile that containsShiftControl/Extras(e.g. written by an older DS4Windows) applies the shift extras to the control's normal slot: they fire without the shift trigger held, clobber any normal extras on the same control, and the actual shift slot stays empty. Combined with bug 3, one load+save cycle makes the loss permanent.I'll open a fix PR covering 3+4 together (they're the two halves of the same round-trip) with a regression test for each direction.
Minor / latent (no runtime effect in current builds, noting for completeness)
AppSettingsDTO.ShouldSerializeCustomLed7StringusesMAX_DS4_CONTROLLER_COUNT > 7where its siblings use>= N— currently equivalent because the constant is 8.AutoProfilesDTO.MapFromsetsController4inside theUSING_MAX_CONTROLLERSblock although slot 4 is a base slot everywhere else — currently unreachable-false, so no effect.