mirror of
https://github.com/FroggMaster/CreamInstaller.git
synced 2026-06-21 22:31:49 -07:00
Improve Logging / Add Forced Proxy for Steam Games with no SteamAPI DLL Closes #28
- Adds logic to allow for a Steam Game with no SteamAPI dll to use forced proxy methods. (This is not guranteed to work and thus these games are tagged with the "Proxy Only" platform tag. - Adds tooltips, and displays them for Unsupported/Proxy Only games if hovered with the mouse. - Adds improved logging to the application, breaks logs into 3 primary logs: game-scan.log, cream-steamcmd.log and CreamInstaller.log
This commit is contained in:
@@ -34,7 +34,8 @@ internal sealed class CustomTreeView : TreeView
|
||||
|
||||
internal CustomTreeView()
|
||||
{
|
||||
DrawMode = TreeViewDrawMode.OwnerDrawAll;
|
||||
ShowNodeToolTips = true;
|
||||
DrawMode = TreeViewDrawMode.OwnerDrawAll;
|
||||
Invalidated += OnInvalidated;
|
||||
DrawNode += DrawTreeNode;
|
||||
Disposed += OnDisposed;
|
||||
@@ -218,7 +219,16 @@ internal sealed class CustomTreeView : TreeView
|
||||
SelectionDLC dlc = SelectionDLC.FromId(dlcType, node.Parent?.Name, id);
|
||||
text = dlc?.Selection != null ? dlc.Selection.Platform.ToString() : dlcType.ToString();
|
||||
}
|
||||
else text = platform.ToString();
|
||||
else
|
||||
{
|
||||
text = platform.ToString();
|
||||
if (platform is Platform.Steam)
|
||||
{
|
||||
Selection selection = Selection.FromId(platform, id);
|
||||
if (selection is not null && selection.SteamApiDllMissing)
|
||||
text = "Proxy Only";
|
||||
}
|
||||
}
|
||||
|
||||
Size size = TextRenderer.MeasureText(graphics, text, font);
|
||||
bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width };
|
||||
@@ -485,6 +495,8 @@ internal sealed class CustomTreeView : TreeView
|
||||
_ = checkBoxBounds.Remove(pair.Key);
|
||||
else if (pair.Value.Contains(clickPoint))
|
||||
{
|
||||
if (pair.Key.SteamApiDllMissing)
|
||||
return;
|
||||
pair.Key.UseProxy = !pair.Key.UseProxy;
|
||||
selectForm?.OnProxyChanged();
|
||||
break;
|
||||
|
||||
@@ -11,6 +11,8 @@ internal sealed partial class DebugForm : CustomForm
|
||||
private static DebugForm current;
|
||||
private static readonly object currentLock = new();
|
||||
|
||||
internal static bool IsOpen { get; private set; }
|
||||
|
||||
private Form attachedForm;
|
||||
|
||||
private DebugForm()
|
||||
@@ -62,6 +64,13 @@ internal sealed partial class DebugForm : CustomForm
|
||||
attachedForm.SizeChanged += OnChange;
|
||||
attachedForm.VisibleChanged += OnChange;
|
||||
UpdateAttachment();
|
||||
|
||||
if (!IsOpen)
|
||||
{
|
||||
IsOpen = true;
|
||||
ProgramData.OnLogWarning += msg => Log(msg, LogTextBox.Warning);
|
||||
ProgramData.OnLogError += msg => Log(msg, LogTextBox.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnChange(object sender, EventArgs args) => UpdateAttachment();
|
||||
|
||||
@@ -189,11 +189,16 @@ internal sealed partial class SelectForm : CustomForm
|
||||
return;
|
||||
HashSet<string> dllDirectories =
|
||||
await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Steam);
|
||||
if (dllDirectories is null)
|
||||
bool steamApiDllMissing = dllDirectories is null;
|
||||
if (steamApiDllMissing)
|
||||
{
|
||||
_ = Interlocked.Decrement(ref steamGamesToCheck);
|
||||
RemoveFromRemainingGames(name);
|
||||
return;
|
||||
dllDirectories = [];
|
||||
if (uninstallAll)
|
||||
{
|
||||
_ = Interlocked.Decrement(ref steamGamesToCheck);
|
||||
RemoveFromRemainingGames(name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (uninstallAll)
|
||||
@@ -348,6 +353,20 @@ internal sealed partial class SelectForm : CustomForm
|
||||
Selection selection = Selection.GetOrCreate(Platform.Steam, appId, storeAppData?.Name ?? name,
|
||||
gameDirectory, dllDirectories,
|
||||
await gameDirectory.GetExecutableDirectories(true));
|
||||
selection.SteamApiDllMissing = steamApiDllMissing;
|
||||
if (steamApiDllMissing)
|
||||
{
|
||||
selection.UseProxy = true;
|
||||
bool has64 = selection.ExecutableDirectories.Any(d => d.binaryType == BinaryType.BIT64);
|
||||
bool has32 = selection.ExecutableDirectories.Any(d => d.binaryType == BinaryType.BIT32);
|
||||
string dllName = (has64, has32) switch
|
||||
{
|
||||
(true, true) => "steam_api.dll / steam_api64.dll",
|
||||
(true, false) => "steam_api64.dll",
|
||||
_ => "steam_api.dll"
|
||||
};
|
||||
selection.TreeNode.ToolTipText = dllName + " was not detected in the game directory. Only proxy installation is available.";
|
||||
}
|
||||
selection.Product = "https://store.steampowered.com/app/" + appId;
|
||||
selection.Icon = IconGrabber.SteamAppImagesPath + @$"\{appId}\{cmdAppData?.Common?.Icon}.jpg";
|
||||
selection.SubIcon = storeAppData?.HeaderImage ?? IconGrabber.SteamAppImagesPath
|
||||
@@ -717,10 +736,7 @@ internal sealed partial class SelectForm : CustomForm
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Handle exceptions in async void to prevent unobserved exceptions
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.WriteLine($"OnLoad exception: {ex.Message}");
|
||||
#endif
|
||||
ProgramData.LogError("SelectForm OnLoad failed", ex);
|
||||
// Show error and clean up
|
||||
ex.HandleException(this);
|
||||
HideProgressBar();
|
||||
@@ -1263,7 +1279,7 @@ internal sealed partial class SelectForm : CustomForm
|
||||
selection.Proxy = currentProxy == Selection.DefaultProxy ? currentProxy : proxy;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!selection.SteamApiDllMissing)
|
||||
{
|
||||
selection.UseProxy = false;
|
||||
selection.Proxy = null;
|
||||
|
||||
@@ -92,7 +92,7 @@ internal sealed partial class TestGameForm : CustomForm
|
||||
if (!string.IsNullOrWhiteSpace(title))
|
||||
return title;
|
||||
}
|
||||
catch { /* fall through to SteamCMD */ }
|
||||
catch (Exception ex) { ProgramData.LogWarning($"[TestGame] Store name lookup failed for AppID {appId}: {ex.Message}"); /* fall through to SteamCMD */ }
|
||||
|
||||
CmdAppData cmdData = await SteamCMD.GetAppInfo(appId);
|
||||
return cmdData?.Common?.Name;
|
||||
@@ -320,7 +320,7 @@ internal sealed partial class TestGameForm : CustomForm
|
||||
SteamLibrary.TestGames.Clear();
|
||||
EpicLibrary.TestManifests.Clear();
|
||||
foreach (string dir in CreatedDirectories)
|
||||
try { Directory.Delete(dir, true); } catch { /* best-effort */ }
|
||||
try { Directory.Delete(dir, true); } catch (Exception ex) { ProgramData.LogWarning($"[TestGame] Cleanup deletion failed for {dir}: {ex.Message}"); }
|
||||
CreatedDirectories.Clear();
|
||||
dlcEntries.Clear();
|
||||
RefreshDlcList();
|
||||
|
||||
@@ -106,12 +106,10 @@ internal sealed partial class UpdateForm : CustomForm
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Handle exceptions in async void to prevent unobserved exceptions
|
||||
ProgramData.LogError("UpdateForm OnLoad failed", ex);
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.WriteLine($"OnLoad exception: {ex.Message}");
|
||||
ex.HandleFatalException();
|
||||
#else
|
||||
// In release, try to continue gracefully
|
||||
StartProgram();
|
||||
#endif
|
||||
}
|
||||
@@ -262,10 +260,7 @@ internal sealed partial class UpdateForm : CustomForm
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Handle exceptions in async void event handler to prevent unobserved exceptions
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.WriteLine($"OnUpdate exception: {ex.Message}");
|
||||
#endif
|
||||
ProgramData.LogError("UpdateForm OnUpdate failed", ex);
|
||||
// Show error to user
|
||||
ex.HandleException(this, Program.Name + " encountered an unexpected error during update");
|
||||
StartProgram();
|
||||
|
||||
@@ -8,9 +8,7 @@ using CreamInstaller.Platforms.Epic.GraphQL;
|
||||
using CreamInstaller.Utility;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
#if DEBUG
|
||||
using CreamInstaller.Forms;
|
||||
#endif
|
||||
|
||||
|
||||
namespace CreamInstaller.Platforms.Epic;
|
||||
|
||||
@@ -33,12 +31,10 @@ internal static class EpicStore
|
||||
if (!cachedExists || ProgramData.CheckCooldown(categoryNamespace, Cooldown))
|
||||
{
|
||||
response = await QueryGraphQL(categoryNamespace);
|
||||
#if DEBUG
|
||||
if (response is null)
|
||||
{
|
||||
DebugForm.Current.Log("ES: QueryGraphQL returned null");
|
||||
ProgramData.LogWarning("Epic QueryGraphQL returned null for " + categoryNamespace);
|
||||
}
|
||||
#endif
|
||||
try
|
||||
{
|
||||
cacheFile.WriteFile(JsonConvert.SerializeObject(response, Formatting.Indented));
|
||||
@@ -191,9 +187,7 @@ internal static class EpicStore
|
||||
HttpClient client = HttpClientManager.HttpClient;
|
||||
if (client is null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log("ES: Client returned null");
|
||||
#endif
|
||||
ProgramData.LogWarning("Epic GraphQL client returned null");
|
||||
return null;
|
||||
}
|
||||
HttpResponseMessage httpResponse =
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CreamInstaller.Forms;
|
||||
using CreamInstaller.Utility;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
@@ -38,61 +37,38 @@ internal static partial class SteamCMD
|
||||
{
|
||||
cacheFile.WriteFile(JsonConvert.SerializeObject(data, Formatting.Indented));
|
||||
}
|
||||
catch
|
||||
#if DEBUG
|
||||
(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugForm.Current.Log("SteamCMD web API query failed on attempt #" + attempts +
|
||||
" for " + appId + (isDlc ? " (DLC)" : "")
|
||||
+ ": Unsuccessful serialization (" + e.Message + ")");
|
||||
ProgramData.LogSteamCmd("SteamCMD web API query failed on attempt #" + attempts +
|
||||
" for " + appId + (isDlc ? " (DLC)" : "")
|
||||
+ ": Unsuccessful serialization (" + e.Message + ")");
|
||||
}
|
||||
#else
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
#endif
|
||||
return data;
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
"SteamCMD web API query failed on attempt #" + attempts + " for " + appId +
|
||||
(isDlc ? " (DLC)" : "")
|
||||
+ ": No data",
|
||||
LogTextBox.Warning);
|
||||
#endif
|
||||
+ ": No data");
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
"SteamCMD web API query failed on attempt #" + attempts + " for " + appId +
|
||||
(isDlc ? " (DLC)" : "")
|
||||
+ ": Status not success (" + appDetails?.Status + ")",
|
||||
LogTextBox.Warning);
|
||||
#endif
|
||||
+ ": Status not success (" + appDetails?.Status + ")");
|
||||
}
|
||||
catch
|
||||
#if DEBUG
|
||||
(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugForm.Current.Log("SteamCMD web API query failed on attempt #" + attempts + " for " +
|
||||
appId + (isDlc ? " (DLC)" : "")
|
||||
+ ": Unsuccessful deserialization (" + e.Message + ")");
|
||||
ProgramData.LogSteamCmd("SteamCMD web API query failed on attempt #" + attempts + " for " +
|
||||
appId + (isDlc ? " (DLC)" : "")
|
||||
+ ": Unsuccessful deserialization (" + e.Message + ")");
|
||||
}
|
||||
#else
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
"SteamCMD web API query failed on attempt #" + attempts + " for " + appId +
|
||||
(isDlc ? " (DLC)" : "") +
|
||||
": Response null",
|
||||
LogTextBox.Warning);
|
||||
#endif
|
||||
": Response null");
|
||||
}
|
||||
|
||||
if (cachedExists)
|
||||
@@ -109,9 +85,7 @@ internal static partial class SteamCMD
|
||||
break;
|
||||
if (attempts > 10)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log("Failed to query SteamCMD web API after 10 tries: " + appId);
|
||||
#endif
|
||||
ProgramData.LogSteamCmd("Failed to query SteamCMD web API after 10 tries: " + appId);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,6 @@ using CreamInstaller.Resources;
|
||||
using CreamInstaller.Utility;
|
||||
using Gameloop.Vdf.JsonConverter;
|
||||
using Gameloop.Vdf.Linq;
|
||||
#if DEBUG
|
||||
using CreamInstaller.Forms;
|
||||
#endif
|
||||
|
||||
namespace CreamInstaller.Platforms.Steam;
|
||||
|
||||
@@ -210,10 +207,7 @@ internal static partial class SteamCMD
|
||||
attempts++;
|
||||
if (attempts > 10)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log("Failed to query SteamCMD after 10 tries: " + appId + " (" + branch + ")",
|
||||
LogTextBox.Warning);
|
||||
#endif
|
||||
ProgramData.LogSteamCmd("Failed to query SteamCMD after 10 tries: " + appId + " (" + branch + ")");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -232,12 +226,9 @@ internal static partial class SteamCMD
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
"SteamCMD query failed on attempt #" + attempts + " for " + appId + " (" + branch +
|
||||
"): Bad output",
|
||||
LogTextBox.Warning);
|
||||
#endif
|
||||
"): Bad output");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -245,12 +236,9 @@ internal static partial class SteamCMD
|
||||
if (!ValveDataFile.TryDeserialize(output, out VProperty appInfo) || appInfo.Value is VValue)
|
||||
{
|
||||
appUpdateFile.DeleteFile();
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
"SteamCMD query failed on attempt #" + attempts + " for " + appId + " (" + branch +
|
||||
"): Deserialization failed",
|
||||
LogTextBox.Warning);
|
||||
#endif
|
||||
"): Deserialization failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -260,29 +248,20 @@ internal static partial class SteamCMD
|
||||
if (appInfo.ToJson().Value.ToObject<CmdAppData>() is not { } cmdAppData)
|
||||
{
|
||||
appUpdateFile.DeleteFile();
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
"SteamCMD query failed on attempt #" + attempts + " for " + appId + " (" + branch +
|
||||
"): VDF-JSON conversion failed",
|
||||
LogTextBox.Warning);
|
||||
#endif
|
||||
"): VDF-JSON conversion failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
appData = cmdAppData;
|
||||
}
|
||||
catch
|
||||
#if DEBUG
|
||||
(Exception e)
|
||||
#endif
|
||||
catch (Exception e)
|
||||
{
|
||||
appUpdateFile.DeleteFile();
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
"SteamCMD query failed on attempt #" + attempts + " for " + appId + " (" + branch +
|
||||
"): VDF-JSON conversion failed (" + e.Message + ")",
|
||||
LogTextBox.Warning);
|
||||
#endif
|
||||
"): VDF-JSON conversion failed (" + e.Message + ")");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -300,11 +279,9 @@ internal static partial class SteamCMD
|
||||
foreach (string dlcAppUpdateFile in dlcAppIds.Select(id => $@"{AppInfoPath}\{id}.vdf"))
|
||||
dlcAppUpdateFile.DeleteFile();
|
||||
appUpdateFile.DeleteFile();
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
"SteamCMD query skipped on attempt #" + attempts + " for " + appId + " (" + branch +
|
||||
"): Outdated cache", LogTextBox.Warning);
|
||||
#endif
|
||||
"): Outdated cache");
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -3,13 +3,10 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using CreamInstaller.Utility;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
#if DEBUG
|
||||
using System;
|
||||
using CreamInstaller.Forms;
|
||||
#endif
|
||||
|
||||
namespace CreamInstaller.Platforms.Steam;
|
||||
|
||||
@@ -18,7 +15,6 @@ internal static class SteamStore
|
||||
private const int CooldownGame = 600;
|
||||
private const int CooldownDlc = 1200;
|
||||
|
||||
#if DEBUG
|
||||
private static string FormatErrorLog(int attempts, string appId, string gameName, bool isDlc, string reason,
|
||||
string parentGameName = null, string parentGameAppId = null)
|
||||
{
|
||||
@@ -35,7 +31,6 @@ internal static class SteamStore
|
||||
string type = isDlc ? "DLC" : "Game";
|
||||
return $"[SteamQuery][Attempt {attempts}][FAILED] AppId: {appId} | Name: \"{gameName}\" | Type: {type} | Reason: {reason}";
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static async Task<HashSet<string>> ParseDlcAppIds(StoreAppData storeAppData)
|
||||
=> await Task.Run(() =>
|
||||
@@ -80,11 +75,8 @@ internal static class SteamStore
|
||||
|
||||
if (!storeAppDetails.Success)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log(
|
||||
FormatErrorLog(attempts, appId, gameName, isDlc, "Query unsuccessful", parentGameName, parentGameAppId),
|
||||
LogTextBox.Warning);
|
||||
#endif
|
||||
ProgramData.LogSteamCmd(
|
||||
FormatErrorLog(attempts, appId, gameName, isDlc, "Query unsuccessful", parentGameName, parentGameAppId));
|
||||
if (data is null)
|
||||
return null;
|
||||
}
|
||||
@@ -95,61 +87,38 @@ internal static class SteamStore
|
||||
{
|
||||
cacheFile.WriteFile(JsonConvert.SerializeObject(data, Formatting.Indented));
|
||||
}
|
||||
catch
|
||||
#if DEBUG
|
||||
(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
FormatErrorLog(attempts, appId, gameName, isDlc, $"Unsuccessful serialization ({e.Message})", parentGameName, parentGameAppId));
|
||||
}
|
||||
#else
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
#endif
|
||||
return data;
|
||||
}
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
FormatErrorLog(attempts, appId, gameName, isDlc, "Response data null", parentGameName, parentGameAppId));
|
||||
#endif
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
{
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
FormatErrorLog(attempts, appId, gameName, isDlc, "Response details null", parentGameName, parentGameAppId));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch
|
||||
#if DEBUG
|
||||
(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
FormatErrorLog(attempts, appId, gameName, isDlc, $"Unsuccessful deserialization ({e.Message})", parentGameName, parentGameAppId));
|
||||
}
|
||||
#else
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
#endif
|
||||
#if DEBUG
|
||||
else
|
||||
{
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
FormatErrorLog(attempts, appId, gameName, isDlc, "Response deserialization null", parentGameName, parentGameAppId));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
{
|
||||
DebugForm.Current.Log(
|
||||
FormatErrorLog(attempts, appId, gameName, isDlc, "Null or empty response", parentGameName, parentGameAppId),
|
||||
LogTextBox.Warning);
|
||||
ProgramData.LogSteamCmd(
|
||||
FormatErrorLog(attempts, appId, gameName, isDlc, "Null or empty response", parentGameName, parentGameAppId));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (cachedExists)
|
||||
@@ -166,10 +135,8 @@ internal static class SteamStore
|
||||
break;
|
||||
if (attempts > 10)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log(
|
||||
ProgramData.LogSteamCmd(
|
||||
FormatErrorLog(attempts, appId, gameName, isDlc, "Maximum retry attempts exceeded (10)", parentGameName, parentGameAppId));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,10 +130,7 @@ internal static class Program
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.WriteLine($"Cleanup failed: {ex.Message}");
|
||||
#endif
|
||||
// Swallow exceptions during fire-and-forget cleanup
|
||||
ProgramData.LogWarning($"Cleanup failed: {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -149,17 +146,12 @@ internal static class Program
|
||||
// Wait up to 5 seconds for graceful cleanup
|
||||
if (!cleanupTask.Wait(TimeSpan.FromSeconds(5)))
|
||||
{
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.WriteLine("Cleanup timed out during application exit");
|
||||
#endif
|
||||
ProgramData.LogWarning("Cleanup timed out during application exit");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.WriteLine($"Cleanup exception during exit: {ex.Message}");
|
||||
#endif
|
||||
// Ignore exceptions during shutdown
|
||||
ProgramData.LogWarning($"Cleanup exception during exit: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -45,6 +45,7 @@ internal sealed class Selection : IEquatable<Selection>
|
||||
internal string SubIcon;
|
||||
internal string Website;
|
||||
internal InstalledUnlocker InstalledUnlocker;
|
||||
internal bool SteamApiDllMissing;
|
||||
|
||||
internal IEnumerable<string> GetAvailableProxies()
|
||||
{
|
||||
@@ -125,9 +126,12 @@ internal sealed class Selection : IEquatable<Selection>
|
||||
return;
|
||||
}
|
||||
|
||||
_ = DllDirectories.RemoveWhere(directory => !directory.DirectoryExists());
|
||||
if (DllDirectories.Count < 1)
|
||||
Remove();
|
||||
if (!SteamApiDllMissing)
|
||||
{
|
||||
_ = DllDirectories.RemoveWhere(directory => !directory.DirectoryExists());
|
||||
if (DllDirectories.Count < 1)
|
||||
Remove();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ValidateAll(List<(Platform platform, string id, string name)> programsToScan)
|
||||
@@ -199,6 +203,24 @@ internal sealed class Selection : IEquatable<Selection>
|
||||
proxy.FileExists() && proxy.IsResourceFile(ResourceIdentifier.Koaloader))
|
||||
|| config.FileExists())
|
||||
return InstalledUnlocker.Koaloader;
|
||||
|
||||
if (Platform is Platform.Steam or Platform.Paradox)
|
||||
{
|
||||
directory.GetSmokeApiComponents(out _, out _, out _, out _, out _, out string smokeConfig, out _, out _, out _);
|
||||
if (smokeConfig.FileExists())
|
||||
return InstalledUnlocker.SmokeAPI;
|
||||
directory.GetCreamApiComponents(out _, out _, out _, out _, out string creamConfig);
|
||||
if (creamConfig.FileExists())
|
||||
return InstalledUnlocker.CreamAPI;
|
||||
if (directory.GetSmokeApiProxies().Any(proxy =>
|
||||
proxy.FileExists() && (proxy.IsResourceFile(ResourceIdentifier.Steamworks32) ||
|
||||
proxy.IsResourceFile(ResourceIdentifier.Steamworks64))))
|
||||
return InstalledUnlocker.SmokeAPI;
|
||||
if (directory.GetCreamApiProxies().Any(proxy =>
|
||||
proxy.FileExists() && (proxy.IsResourceFile(ResourceIdentifier.Steamworks32) ||
|
||||
proxy.IsResourceFile(ResourceIdentifier.Steamworks64))))
|
||||
return InstalledUnlocker.CreamAPI;
|
||||
}
|
||||
}
|
||||
|
||||
return InstalledUnlocker.None;
|
||||
|
||||
@@ -5,9 +5,7 @@ using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
#if DEBUG
|
||||
using CreamInstaller.Forms;
|
||||
#endif
|
||||
|
||||
|
||||
namespace CreamInstaller.Utility;
|
||||
|
||||
@@ -87,44 +85,28 @@ internal static class HttpClientManager
|
||||
{
|
||||
if (e.StatusCode != HttpStatusCode.TooManyRequests)
|
||||
{
|
||||
#if DEBUG
|
||||
string statusInfo = e.StatusCode.HasValue ? $" (HTTP {(int)e.StatusCode.Value})" : "";
|
||||
DebugForm.Current.Log($"Get request failed to {url}{statusInfo}: {e}", LogTextBox.Warning);
|
||||
#endif
|
||||
ProgramData.LogWarning($"Get request failed to {url}{statusInfo}: {e.Message}");
|
||||
return null;
|
||||
}
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log($"Too many requests to {url} (HTTP 429 - Rate Limited)", LogTextBox.Error);
|
||||
#endif
|
||||
// do something special?
|
||||
ProgramData.LogWarning($"Too many requests to {url} (HTTP 429 - Rate Limited)");
|
||||
return null;
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log("Get request timed out for " + url, LogTextBox.Warning);
|
||||
#endif
|
||||
ProgramData.LogWarning("Get request timed out for " + url);
|
||||
return null;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugForm.Current.Log("Get request was cancelled for " + url, LogTextBox.Warning);
|
||||
#endif
|
||||
ProgramData.LogWarning("Get request was cancelled for " + url);
|
||||
return null;
|
||||
}
|
||||
#if DEBUG
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugForm.Current.Log("Get request failed to " + url + ": " + e, LogTextBox.Warning);
|
||||
ProgramData.LogWarning("Get request failed to " + url + ": " + e.Message);
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static async Task<Image> GetImageFromUrl(string url)
|
||||
|
||||
@@ -64,7 +64,12 @@ internal static class ProgramData
|
||||
private static readonly string ExtraProtectionChoicesPath = DirectoryPath + @"\extraprotection.json";
|
||||
private static readonly string InstalledGamesPath = DirectoryPath + @"\installed.json";
|
||||
|
||||
internal static readonly string LogPath = DirectoryPath + @"\scan.log";
|
||||
internal static readonly string ScanLogPath = Path.Combine(DirectoryPath, "game-scan.log");
|
||||
internal static readonly string SteamCmdLogPath = Path.Combine(DirectoryPath, "cream-steamcmd.log");
|
||||
internal static readonly string AppLogPath = Path.Combine(DirectoryPath, "CreamInstaller.log");
|
||||
|
||||
internal static event Action<string> OnLogWarning;
|
||||
internal static event Action<string> OnLogError;
|
||||
|
||||
private static readonly object LogLock = new();
|
||||
|
||||
@@ -75,7 +80,7 @@ internal static class ProgramData
|
||||
string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture);
|
||||
string entry = $"[{timestamp}] {message}{Environment.NewLine}";
|
||||
lock (LogLock)
|
||||
File.AppendAllText(LogPath, entry, Encoding.UTF8);
|
||||
File.AppendAllText(ScanLogPath, entry, Encoding.UTF8);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -83,12 +88,61 @@ internal static class ProgramData
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LogSteamCmd(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture);
|
||||
string entry = $"[{timestamp}] [SteamCMD] {message}{Environment.NewLine}";
|
||||
lock (LogLock)
|
||||
File.AppendAllText(SteamCmdLogPath, entry, Encoding.UTF8);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored — logging must never crash the application
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LogWarning(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture);
|
||||
string entry = $"[{timestamp}] [WARN] {message}{Environment.NewLine}";
|
||||
lock (LogLock)
|
||||
File.AppendAllText(AppLogPath, entry, Encoding.UTF8);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored — logging must never crash the application
|
||||
}
|
||||
OnLogWarning?.Invoke(message);
|
||||
}
|
||||
|
||||
internal static void LogError(string message, Exception ex = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture);
|
||||
string entry = ex is not null
|
||||
? $"[{timestamp}] [ERROR] {message}{Environment.NewLine}[{timestamp}] [ERROR] Exception: {ex}{Environment.NewLine}"
|
||||
: $"[{timestamp}] [ERROR] {message}{Environment.NewLine}";
|
||||
lock (LogLock)
|
||||
File.AppendAllText(AppLogPath, entry, Encoding.UTF8);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored — logging must never crash the application
|
||||
}
|
||||
OnLogError?.Invoke(message);
|
||||
}
|
||||
|
||||
internal static void ClearLog()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(LogPath))
|
||||
File.Delete(LogPath);
|
||||
if (File.Exists(ScanLogPath))
|
||||
File.Delete(ScanLogPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -335,7 +335,7 @@ internal static class ThemeManager
|
||||
int useDark = IsDark ? 1 : 0;
|
||||
NativeMethods.EnableDarkTitleBar(form.Handle, useDark);
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex) { ProgramData.LogWarning($"[Theme] Title bar theming failed: {ex.Message}"); }
|
||||
}
|
||||
|
||||
private static void TryApplyScrollbarTheme(Control control, bool dark)
|
||||
@@ -345,7 +345,7 @@ internal static class ThemeManager
|
||||
string theme = dark ? "DarkMode_Explorer" : null;
|
||||
NativeImports.SetWindowTheme(control.Handle, theme, null);
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex) { ProgramData.LogWarning($"[Theme] Scrollbar theming failed: {ex.Message}"); }
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
@@ -454,7 +454,7 @@ internal static class ThemeManager
|
||||
// button is centralized here so theming resides in ThemeManager.
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
// Dark checkbox colors – matched to how the system renders the "All" CheckBox control
|
||||
// Dark checkbox colors � matched to how the system renders the "All" CheckBox control
|
||||
// in dark mode: dark fill, mid-gray border, light foreground tick.
|
||||
private static readonly Color DarkCbBorder = ColorTranslator.FromHtml("#6B6B6B");
|
||||
private static readonly Color DarkCbDisabledBorder = ColorTranslator.FromHtml("#454545");
|
||||
@@ -478,7 +478,7 @@ internal static class ThemeManager
|
||||
|
||||
if (isChecked && enabled)
|
||||
{
|
||||
// Checked + enabled: accent fill, no border, white tick — matches Windows 11 dark CheckBox
|
||||
// Checked + enabled: accent fill, no border, white tick � matches Windows 11 dark CheckBox
|
||||
using SolidBrush fillBrush = new(Accent);
|
||||
g.FillPath(fillBrush, path);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user