Further Enhancements to Recall Games Installed With DLC Unlockers

- Rename JSON Proxy field to "Proxy DLL Name" and always store the resolved
  name instead of null for default convention
- Add DetectInstalledProxy() to find proxy dlls winmm/winhttp/version.dll
- Fixes to ensure all detected unlockers are persisted in installed.json on scan so pre-existing unlockers are tracked even if not installed via CreamInstaller
- Restore proxy and extra protection state from saved records for both
  scanned and previously scanned games on reload
- Detect proxy DLLs from disk for previously scanned game reconstruction
This commit is contained in:
Frog
2026-06-15 02:55:24 -07:00
parent 121236632c
commit 0c3edf1ee7
4 changed files with 67 additions and 17 deletions
+1 -1
View File
@@ -371,7 +371,7 @@ internal sealed partial class InstallForm : CustomForm
RootDirectory = selection.RootDirectory,
Unlocker = unlocker,
UseProxy = selection.UseProxy,
Proxy = selection.Proxy,
ProxyDllName = selection.UseProxy ? selection.Proxy ?? Selection.DefaultProxy : null,
UseExtraProtection = selection.UseExtraProtection,
Dlc = selection.DLC.Select(dlc => new InstalledDlcRecord
{
+44 -10
View File
@@ -1111,16 +1111,23 @@ internal sealed partial class SelectForm : CustomForm
List<InstalledGameRecord> toRemove = [];
foreach (InstalledGameRecord record in saved)
{
// Already in the list from this scan — just ensure unlocker is set
// Already in the list from this scan; ensure unlocker, proxy, and extra protection are set
Selection existing = Selection.FromId(record.Platform, record.Id);
if (existing is not null)
{
if (existing.InstalledUnlocker == InstalledUnlocker.None)
existing.InstalledUnlocker = record.Unlocker;
if (record.UseProxy)
{
existing.UseProxy = true;
existing.Proxy = record.ProxyDllName;
}
if (record.UseExtraProtection)
existing.UseExtraProtection = true;
continue;
}
// Root directory no longer exists mark for removal
// Root directory no longer exists mark for removal
if (!record.RootDirectory.DirectoryExists())
{
toRemove.Add(record);
@@ -1144,8 +1151,20 @@ internal sealed partial class SelectForm : CustomForm
selection.InstalledUnlocker = selection.DetectInstalledUnlocker();
if (selection.InstalledUnlocker == InstalledUnlocker.None)
selection.InstalledUnlocker = record.Unlocker;
selection.UseProxy = record.UseProxy;
selection.Proxy = record.Proxy;
if (selection.InstalledUnlocker != InstalledUnlocker.None)
{
string detectedProxy = selection.DetectInstalledProxy();
if (detectedProxy is not null)
{
selection.UseProxy = true;
selection.Proxy = detectedProxy;
}
else
{
selection.UseProxy = record.UseProxy;
selection.Proxy = record.ProxyDllName;
}
}
selection.UseExtraProtection = record.UseExtraProtection;
Invoke(delegate
@@ -1382,9 +1401,20 @@ internal sealed partial class SelectForm : CustomForm
ProgramData.WriteExtraProtectionChoices(extraProtectionChoices);
loadButton.Enabled = CanLoadSelections();
// Detect installed unlockers from disk for all selections
// Detect installed unlockers and proxy DLLs from disk for all selections
foreach (Selection selection in Selection.All.Keys)
{
selection.InstalledUnlocker = selection.DetectInstalledUnlocker();
if (selection.InstalledUnlocker != InstalledUnlocker.None)
{
string detectedProxy = selection.DetectInstalledProxy();
if (detectedProxy is not null)
{
selection.UseProxy = true;
selection.Proxy = detectedProxy;
}
}
}
// Merge with persisted installed game records for any saved games not yet having a detected unlocker
List<InstalledGameRecord> installedRecords = ProgramData.ReadInstalledGames();
@@ -1397,11 +1427,14 @@ internal sealed partial class SelectForm : CustomForm
selection.InstalledUnlocker = record.Unlocker;
}
// Persist all selections with a detected DLC unlocker to installed.json, so they are tracked
// even when the unlocker was installed outside of CreamInstaller
// Persist any selections with a detected unlocker to installed.json, preserving existing
// proxy/extrapolation data from the saved record so detection does not overwrite prior install state
foreach (Selection selection in Selection.All.Keys)
{
if (selection.InstalledUnlocker != InstalledUnlocker.None)
{
InstalledGameRecord existing = installedRecords.FirstOrDefault(r =>
r.Platform == selection.Platform && r.Id == selection.Id);
ProgramData.UpsertInstalledGame(new InstalledGameRecord
{
Platform = selection.Platform,
@@ -1409,9 +1442,9 @@ internal sealed partial class SelectForm : CustomForm
Name = selection.Name,
RootDirectory = selection.RootDirectory,
Unlocker = selection.InstalledUnlocker,
UseProxy = selection.UseProxy,
Proxy = selection.Proxy,
UseExtraProtection = selection.UseExtraProtection,
UseProxy = existing?.UseProxy ?? false,
ProxyDllName = existing?.UseProxy == true ? existing.ProxyDllName : null,
UseExtraProtection = existing?.UseExtraProtection ?? false,
Dlc = selection.DLC.Select(dlc => new InstalledDlcRecord
{
DlcType = dlc.Type.ToString(),
@@ -1419,6 +1452,7 @@ internal sealed partial class SelectForm : CustomForm
Name = dlc.Name
}).ToList()
});
}
}
OnProxyChanged();
+14 -1
View File
@@ -163,7 +163,7 @@ internal sealed class Selection : IEquatable<Selection>
return InstalledUnlocker.CreamAPI;
}
// Fallback: config was deleted but _o files remain identify by replacement DLL content
// Fallback: config was deleted but _o files remain identify by replacement DLL content
directory.GetSmokeApiComponents(out string smokeApi32, out string api32_o,
out string smokeApi64, out string api64_o, out _, out _, out _, out _, out _);
if (api32_o.FileExists() || api64_o.FileExists())
@@ -226,6 +226,19 @@ internal sealed class Selection : IEquatable<Selection>
return InstalledUnlocker.None;
}
internal string DetectInstalledProxy()
{
HashSet<string> knownProxies = ["winmm", "winhttp", "version"];
foreach (string directory in DllDirectories)
foreach (string proxy in knownProxies)
{
string proxyPath = directory + @"\" + proxy + ".dll";
if (proxyPath.FileExists())
return proxy;
}
return null;
}
private void ReadCreamApiConfig(string configPath)
{
try
+8 -5
View File
@@ -37,7 +37,10 @@ internal sealed class InstalledGameRecord
public string RootDirectory { get; set; }
public InstalledUnlocker Unlocker { get; set; }
public bool UseProxy { get; set; }
public string Proxy { get; set; }
[JsonProperty("Proxy DLL Name")]
public string ProxyDllName { get; set; }
public bool UseExtraProtection { get; set; }
public List<InstalledDlcRecord> Dlc { get; set; } = [];
}
@@ -84,7 +87,7 @@ internal static event Action<string> OnLogError;
}
catch
{
// ignored logging must never crash the application
// ignored; logging must never crash the application
}
}
@@ -99,7 +102,7 @@ internal static event Action<string> OnLogError;
}
catch
{
// ignored logging must never crash the application
// ignored; logging must never crash the application
}
}
@@ -114,7 +117,7 @@ internal static event Action<string> OnLogError;
}
catch
{
// ignored logging must never crash the application
// ignored; logging must never crash the application
}
OnLogWarning?.Invoke(message);
}
@@ -132,7 +135,7 @@ internal static event Action<string> OnLogError;
}
catch
{
// ignored logging must never crash the application
// ignored; logging must never crash the application
}
OnLogError?.Invoke(message);
}