Skip to content

Commit 1acc253

Browse files
carterscodeclaude
andcommitted
feat: detailed changes.log with raw registry values, README reference
User asked for changes.log to show exactly what registry changes are made. Previous format was a single line with friendly state names ("Default -> Disabled (gaming)") which didn't show the actual DWORDs or REG_SZs being written. New format is multi-line and includes: - Mechanism: full registry path + value type, or Win32 API name - Before: raw bytes/number/string + friendly description - Wrote: raw target value + friendly description - After: what was re-read post-apply (verified vs NOT VERIFIED) - Reboot: only present for reboot-required settings - Verify: a PowerShell one-liner the user can paste themselves Sample entry: ---------------------------------------------------------------- [2026-05-06 22:14:08] [manual] OK USB Selective Suspend (global override) Mechanism : HKLM\SYSTEM\CurrentControlSet\Services\USB\DisableSelectiveSuspend (DWORD) Before : 0 (Default) Wrote : 1 (Disabled (gaming)) After : 1 (Disabled (gaming)) <- verified Reboot : required to take effect Verify : (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\USB' -Name DisableSelectiveSuspend).DisableSelectiveSuspend Implementation: - DriftItem + ApplyResult records gain RawBefore / RawDesired (and ApplyResult adds RawAfter, computed by re-reading via ChangeApplier post-apply). - Each of the 15 monitors now populates raw values: - DWORDs as decimal numbers (HAGS, MemInt, USB SS, Game Mode, Game DVR, Sys Resp, Network Throttling) - 0xFFFFFFFF for the magic disabled-network-throttling value - REG_SZ pairs for Games multimedia task profile - GUIDs for power plan - DEVMODE struct fields for refresh rate / resolution - DisplayConfig flags for HDR - Multi-value compositions for FSO / VRR / Game Mode (which write to multiple keys at once) - ChangeLogger.Format builds the multi-line block with the new fields. RotateIfNeeded behavior unchanged. README gains a top-level "Change log format" section with the schema, a fields table, and three worked examples (verified manual apply, verified auto-apply, failed apply with UAC cancel). TOC updated. Verification section's "change log" bullet now links to the new section instead of inlining a single sample. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 2885e5f commit 1acc253

20 files changed

Lines changed: 209 additions & 56 deletions

README.md

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ It's also paranoid about not making your gaming worse. Polling pauses entirely d
4242
- [Usage](#usage)
4343
- [Performance & gaming impact](#performance--gaming-impact)
4444
- [How it works](#how-it-works)
45+
- [Verification](#verification--how-do-you-know-its-actually-doing-what-it-says)
46+
- [Change log format](#change-log-format)
47+
- [Files GamerGuardian writes](#files-gamerguardian-writes)
4548
- [Build from source](#build-from-source)
4649
- [Compatibility](#compatibility)
4750
- [Contributing](#contributing)
@@ -225,19 +228,82 @@ GamerGuardian doesn't ask you to take its word for it.
225228
| Power plan | `powercfg /getactivescheme` |
226229
| HDR / Refresh / Resolution | Settings → System → Display, or `dxdiag` |
227230

228-
**4. The change log.** `%APPDATA%\GamerGuardian\changes.log` — every applied change (manual or silent auto-apply) is appended with a timestamp, source, before/after values, and verification status. Open it from *Settings → General → Open change log*, or from the Apply Results window. Auto-rotates at ~1 MB.
231+
**4. The change log.** `%APPDATA%\GamerGuardian\changes.log` — every applied change (manual or silent auto-apply) is appended with full detail of the actual registry write or API call. See [Change log format](#change-log-format) below for the full schema and a worked example. Open it from *Settings → General → Open change log* or from the Apply Results window. Auto-rotates at ~1 MB.
229232

230-
Sample line:
233+
**5. The `--test` CLI flag** (`GamerGuardian.exe --test`) writes every monitor's current readout to `%TEMP%\gamerguardian_selftest.txt` — same call paths the live UI uses, so the file is always in sync with what Settings shows.
234+
235+
**6. The source.** Every monitor is a single file under [`src/GamerGuardian/Monitors/`](src/GamerGuardian/Monitors/) that does exactly one thing each. Read [`HagsMonitor.cs`](src/GamerGuardian/Monitors/HagsMonitor.cs), say, to see the full code that reads and writes HAGS — about 30 lines.
236+
237+
## Change log format
238+
239+
`%APPDATA%\GamerGuardian\changes.log` is the authoritative record of every registry write or API call GamerGuardian makes. Each entry is multi-line and shows exactly what was touched and what the raw values were. Format:
231240

232241
```
233-
[2026-05-06 22:14:08] [manual] OK USB Selective Suspend (global override) | Default -> Disabled (gaming) | now: Disabled (gaming) | reboot pending
242+
--------------------------------------------------------------------------------
243+
[YYYY-MM-DD HH:MM:SS] [source] STATUS Description
244+
Mechanism : <registry path or API call>
245+
Before : <raw before value> (<friendly description>)
246+
Wrote : <raw target value> (<friendly description>)
247+
After : <raw after value> (<friendly description>) <- verified | NOT VERIFIED
248+
Reboot : required to take effect (only for reboot-required settings)
249+
Verify : <PowerShell snippet to read the same value yourself>
234250
```
235251

236-
Auto-rotates at ~1 MB (`changes.log.1` keeps the previous batch).
252+
Fields:
237253

238-
**5. The `--test` CLI flag** (`GamerGuardian.exe --test`) writes every monitor's current readout to `%TEMP%\gamerguardian_selftest.txt` — same call paths the live UI uses, so the file is always in sync with what Settings shows.
254+
| Field | Meaning |
255+
|---|---|
256+
| **timestamp** | Local time of the apply |
257+
| **source** | `manual` (you clicked Apply) or `auto` (silent auto-apply during a poll tick) |
258+
| **STATUS** | `OK` if the after-read matched the target; `FAILED` otherwise (UAC declined, registry locked, etc.) |
259+
| **Mechanism** | The exact registry path + value type, or the Win32 / DLL function called for non-registry settings |
260+
| **Before** | The raw bytes / number / string GamerGuardian read before the write, plus a parenthesized friendly description |
261+
| **Wrote** | The raw value GamerGuardian asked the OS to set |
262+
| **After** | What GamerGuardian saw when it re-read the value post-apply. Should equal **Wrote** for verified entries. |
263+
| **Reboot** | Present only when the setting needs a Windows restart to actually take effect |
264+
| **Verify** | A PowerShell one-liner you can paste into a fresh terminal to read the same value yourself |
239265

240-
**6. The source.** Every monitor is a single file under [`src/GamerGuardian/Monitors/`](src/GamerGuardian/Monitors/) that does exactly one thing each. Read [`HagsMonitor.cs`](src/GamerGuardian/Monitors/HagsMonitor.cs), say, to see the full code that reads and writes HAGS — about 30 lines.
266+
### Worked example
267+
268+
A manual Apply that flipped USB Selective Suspend to gaming-optimized:
269+
270+
```
271+
--------------------------------------------------------------------------------
272+
[2026-05-06 22:14:08] [manual] OK USB Selective Suspend (global override)
273+
Mechanism : HKLM\SYSTEM\CurrentControlSet\Services\USB\DisableSelectiveSuspend (DWORD)
274+
Before : 0 (Default)
275+
Wrote : 1 (Disabled (gaming))
276+
After : 1 (Disabled (gaming)) <- verified
277+
Reboot : required to take effect
278+
Verify : (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\USB' -Name DisableSelectiveSuspend).DisableSelectiveSuspend
279+
```
280+
281+
A silent auto-apply that fixed Game Mode after a Windows update reset it:
282+
283+
```
284+
--------------------------------------------------------------------------------
285+
[2026-05-07 09:14:33] [auto ] OK Windows Game Mode
286+
Mechanism : HKCU\Software\Microsoft\GameBar\AutoGameModeEnabled / AllowAutoGameMode
287+
Before : AutoGameModeEnabled=0, AllowAutoGameMode=0 (Off)
288+
Wrote : AutoGameModeEnabled=1, AllowAutoGameMode=1 (On)
289+
After : AutoGameModeEnabled=1, AllowAutoGameMode=1 (On) <- verified
290+
Verify : (Get-ItemProperty 'HKCU:\Software\Microsoft\GameBar' -Name AutoGameModeEnabled).AutoGameModeEnabled
291+
```
292+
293+
A failed Apply where the user cancelled the UAC prompt:
294+
295+
```
296+
--------------------------------------------------------------------------------
297+
[2026-05-07 09:18:02] [manual] FAILED Memory Integrity (Core Isolation) — requires reboot
298+
Mechanism : HKLM\SYSTEM\...\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity\Enabled (DWORD)
299+
Before : 1 (On)
300+
Wrote : 0 (Off)
301+
After : 1 (On) <- NOT VERIFIED
302+
Reboot : required to take effect
303+
Verify : (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity' -Name Enabled).Enabled
304+
```
305+
306+
The `<- NOT VERIFIED` marker plus matching Before/After tells you the write didn't land. For HKLM-write settings, the most common cause is a UAC cancel.
241307

242308
## Files GamerGuardian writes
243309

src/GamerGuardian/Models/ApplyResult.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ public sealed record ApplyResult(
99
bool Verified,
1010
bool RequiresReboot,
1111
string Mechanism,
12-
string VerifyCommand);
12+
string VerifyCommand,
13+
string RawBefore = "",
14+
string RawDesired = "",
15+
string RawAfter = "");

src/GamerGuardian/Models/DriftReport.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ public sealed record DriftItem(
1010
bool AutoApply,
1111
Func<Task> Apply,
1212
bool RequiresReboot = false,
13-
bool IsMonitored = true);
13+
bool IsMonitored = true,
14+
string RawBefore = "",
15+
string RawDesired = "");
1416

1517
public sealed record DriftReport(IReadOnlyList<DriftItem> Items)
1618
{

src/GamerGuardian/Monitors/FullscreenOptimizationsMonitor.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ public IEnumerable<DriftItem> CheckDrift(AppConfig config)
1717
if (current.Value == pref.DesiredOn) yield break;
1818

1919
bool desired = pref.DesiredOn;
20+
string rawBefore = current.Value
21+
? "FSEBehaviorMode=0, DXGIHonorFSEWindowsCompatible=1, HonorUserFSEBehaviorMode=0, EFSEFeatureFlags=0"
22+
: "FSEBehaviorMode=2, DXGIHonorFSEWindowsCompatible=0, HonorUserFSEBehaviorMode=1, EFSEFeatureFlags=0";
23+
string rawDesired = desired
24+
? "FSEBehaviorMode=0, DXGIHonorFSEWindowsCompatible=1, HonorUserFSEBehaviorMode=0, EFSEFeatureFlags=0"
25+
: "FSEBehaviorMode=2, DXGIHonorFSEWindowsCompatible=0, HonorUserFSEBehaviorMode=1, EFSEFeatureFlags=0";
2026
yield return new DriftItem(
2127
SettingId: Id,
2228
DisplayKey: "global",
@@ -26,7 +32,9 @@ public IEnumerable<DriftItem> CheckDrift(AppConfig config)
2632
DesiredValue: desired ? "On" : "Off",
2733
AutoApply: pref.AutoApply,
2834
Apply: () => Task.Run(() => Apply(desired)),
29-
IsMonitored: pref.Monitor);
35+
IsMonitored: pref.Monitor,
36+
RawBefore: rawBefore,
37+
RawDesired: rawDesired);
3038
}
3139

3240
public static bool? ReadCurrent()

src/GamerGuardian/Monitors/GameDvrMonitor.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public IEnumerable<DriftItem> CheckDrift(AppConfig config)
1616
if (current.Value == pref.DesiredOn) yield break;
1717

1818
bool desired = pref.DesiredOn;
19+
int rawBefore = current.Value ? 1 : 0;
20+
int rawDesired = desired ? 1 : 0;
1921
yield return new DriftItem(
2022
SettingId: Id,
2123
DisplayKey: "global",
@@ -25,7 +27,9 @@ public IEnumerable<DriftItem> CheckDrift(AppConfig config)
2527
DesiredValue: desired ? "On" : "Off",
2628
AutoApply: pref.AutoApply,
2729
Apply: () => Task.Run(() => Apply(desired)),
28-
IsMonitored: pref.Monitor);
30+
IsMonitored: pref.Monitor,
31+
RawBefore: $"GameDVR_Enabled={rawBefore}, AppCaptureEnabled={rawBefore}",
32+
RawDesired: $"GameDVR_Enabled={rawDesired}, AppCaptureEnabled={rawDesired}");
2933
}
3034

3135
public static bool? ReadCurrent()

src/GamerGuardian/Monitors/GameModeMonitor.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public IEnumerable<DriftItem> CheckDrift(AppConfig config)
1717
if (current.Value == pref.DesiredOn) yield break;
1818

1919
bool desired = pref.DesiredOn;
20+
int rawBefore = current.Value ? 1 : 0;
21+
int rawDesired = desired ? 1 : 0;
2022
yield return new DriftItem(
2123
SettingId: Id,
2224
DisplayKey: "global",
@@ -26,7 +28,9 @@ public IEnumerable<DriftItem> CheckDrift(AppConfig config)
2628
DesiredValue: desired ? "On" : "Off",
2729
AutoApply: pref.AutoApply,
2830
Apply: () => Task.Run(() => Apply(desired)),
29-
IsMonitored: pref.Monitor);
31+
IsMonitored: pref.Monitor,
32+
RawBefore: $"AutoGameModeEnabled={rawBefore}, AllowAutoGameMode={rawBefore}",
33+
RawDesired: $"AutoGameModeEnabled={rawDesired}, AllowAutoGameMode={rawDesired}");
3034
}
3135

3236
public static bool? ReadCurrent()

src/GamerGuardian/Monitors/GamesTaskProfileMonitor.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public IEnumerable<DriftItem> CheckDrift(AppConfig config)
2626
if (current.Value == pref.DesiredOn) yield break;
2727

2828
bool desired = pref.DesiredOn;
29+
string rawDesired = string.Join(", ", Values.Select(v => $"{v.Name}={(desired ? v.Gaming : v.Default)}"));
30+
string rawBefore = string.Join(", ", Values.Select(v => $"{v.Name}={(current.Value ? v.Gaming : v.Default)}"));
2931
yield return new DriftItem(
3032
SettingId: Id,
3133
DisplayKey: "global",
@@ -35,7 +37,9 @@ public IEnumerable<DriftItem> CheckDrift(AppConfig config)
3537
DesiredValue: desired ? "Gaming" : "Default",
3638
AutoApply: pref.AutoApply,
3739
Apply: () => Task.Run(() => ApplyValues(desired)),
38-
IsMonitored: pref.Monitor);
40+
IsMonitored: pref.Monitor,
41+
RawBefore: rawBefore,
42+
RawDesired: rawDesired);
3943
}
4044

4145
public static bool? ReadCurrent()

src/GamerGuardian/Monitors/HagsMonitor.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,26 @@ public IEnumerable<DriftItem> CheckDrift(AppConfig config)
1414
{
1515
var pref = config.Global.Hags;
1616

17-
var current = ReadCurrent();
18-
if (current is null) yield break;
19-
if (current.Value == pref.DesiredOn) yield break;
17+
using var k = Registry.LocalMachine.OpenSubKey(SubKey, writable: false);
18+
if (k?.GetValue(ValueName) is not int rawValue) yield break;
19+
var current = rawValue == 2;
20+
if (current == pref.DesiredOn) yield break;
2021

2122
bool desired = pref.DesiredOn;
23+
uint desiredRaw = desired ? 2u : 1u;
2224
yield return new DriftItem(
2325
SettingId: Id,
2426
DisplayKey: "global",
2527
DisplayLabel: "Global",
2628
Description: "Hardware-accelerated GPU Scheduling (HAGS) — requires reboot",
27-
CurrentValue: current.Value ? "On" : "Off",
29+
CurrentValue: current ? "On" : "Off",
2830
DesiredValue: desired ? "On" : "Off",
2931
AutoApply: pref.AutoApply,
30-
Apply: () => Task.Run(() => ElevatedRegistry.SetHklmDword(SubKey, ValueName, desired ? 2u : 1u)),
32+
Apply: () => Task.Run(() => ElevatedRegistry.SetHklmDword(SubKey, ValueName, desiredRaw)),
3133
RequiresReboot: true,
32-
IsMonitored: pref.Monitor);
34+
IsMonitored: pref.Monitor,
35+
RawBefore: rawValue.ToString(),
36+
RawDesired: desiredRaw.ToString());
3337
}
3438

3539
public static bool? ReadCurrent()

src/GamerGuardian/Monitors/HdrMonitor.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ public IEnumerable<DriftItem> CheckDrift(AppConfig config)
3131
DesiredValue: desired ? "On" : "Off",
3232
AutoApply: pref.Hdr.AutoApply,
3333
Apply: () => Task.Run(() => SetHdrState(display, desired)),
34-
IsMonitored: pref.Hdr.Monitor);
34+
IsMonitored: pref.Hdr.Monitor,
35+
RawBefore: $"advancedColorEnabled={(current ? 1 : 0)}",
36+
RawDesired: $"advancedColorEnabled={(desired ? 1 : 0)}");
3537
}
3638
}
3739

src/GamerGuardian/Monitors/MemoryIntegrityMonitor.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,26 @@ public IEnumerable<DriftItem> CheckDrift(AppConfig config)
1515
{
1616
var pref = config.Global.MemoryIntegrity;
1717

18-
var current = ReadCurrent();
19-
if (current is null) yield break;
20-
if (current.Value == pref.DesiredOn) yield break;
18+
using var k = Registry.LocalMachine.OpenSubKey(SubKey, writable: false);
19+
if (k?.GetValue(ValueName) is not int rawValue) yield break;
20+
var current = rawValue != 0;
21+
if (current == pref.DesiredOn) yield break;
2122

2223
bool desired = pref.DesiredOn;
24+
uint desiredRaw = desired ? 1u : 0u;
2325
yield return new DriftItem(
2426
SettingId: Id,
2527
DisplayKey: "global",
2628
DisplayLabel: "Global",
2729
Description: "Memory Integrity (Core Isolation) — requires reboot",
28-
CurrentValue: current.Value ? "On" : "Off",
30+
CurrentValue: current ? "On" : "Off",
2931
DesiredValue: desired ? "On" : "Off",
3032
AutoApply: pref.AutoApply,
31-
Apply: () => Task.Run(() => ElevatedRegistry.SetHklmDword(SubKey, ValueName, desired ? 1u : 0u)),
33+
Apply: () => Task.Run(() => ElevatedRegistry.SetHklmDword(SubKey, ValueName, desiredRaw)),
3234
RequiresReboot: true,
33-
IsMonitored: pref.Monitor);
35+
IsMonitored: pref.Monitor,
36+
RawBefore: rawValue.ToString(),
37+
RawDesired: desiredRaw.ToString());
3438
}
3539

3640
public static bool? ReadCurrent()

0 commit comments

Comments
 (0)