Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions GamesDat/Telemetry/Sources/Formula1/F12022RaceReplaySource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace GamesDat.Core.Telemetry.Sources.Formula1
{
/// <summary>
/// File watcher source for F1 2022 race replay files.
/// Monitors: Documents\My Games\F1 22\replays
/// </summary>
public class F12022RaceReplaySource : F1RaceReplaySourceBase
{
public F12022RaceReplaySource(FileWatcherOptions options)
: base(ApplyDefaults(options))
{
}

public F12022RaceReplaySource(string? customPath = null)
: base(CreateDefaultOptions(customPath ?? GetDefaultReplayPath()))
{
}

public static string GetDefaultReplayPath() => GetReplayPathForYear("22");

/// <summary>
/// Ensure options has a path set, defaulting to F1 22 if not provided
/// </summary>
private static FileWatcherOptions EnsurePath(FileWatcherOptions options)
{
if (string.IsNullOrEmpty(options.Path))
{
return new FileWatcherOptions
{
Path = GetDefaultReplayPath(),
Patterns = options.Patterns,
IncludeSubdirectories = options.IncludeSubdirectories,
DebounceDelay = options.DebounceDelay
};
}
return options;
}

/// <summary>
/// Apply F1 22-specific defaults. Used by test discovery.
/// </summary>
private static new FileWatcherOptions ApplyDefaults(FileWatcherOptions options)
{
return F1RaceReplaySourceBase.ApplyDefaults(EnsurePath(options));
}
}
}
47 changes: 47 additions & 0 deletions GamesDat/Telemetry/Sources/Formula1/F12023RaceReplaySource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace GamesDat.Core.Telemetry.Sources.Formula1
{
/// <summary>
/// File watcher source for F1 2023 race replay files.
/// Monitors: Documents\My Games\F1 23\replays
/// </summary>
public class F12023RaceReplaySource : F1RaceReplaySourceBase
{
public F12023RaceReplaySource(FileWatcherOptions options)
: base(ApplyDefaults(options))
{
}

public F12023RaceReplaySource(string? customPath = null)
: base(CreateDefaultOptions(customPath ?? GetDefaultReplayPath()))
{
}

public static string GetDefaultReplayPath() => GetReplayPathForYear("23");

/// <summary>
/// Ensure options has a path set, defaulting to F1 23 if not provided
/// </summary>
private static FileWatcherOptions EnsurePath(FileWatcherOptions options)
{
if (string.IsNullOrEmpty(options.Path))
{
return new FileWatcherOptions
{
Path = GetDefaultReplayPath(),
Patterns = options.Patterns,
IncludeSubdirectories = options.IncludeSubdirectories,
DebounceDelay = options.DebounceDelay
};
}
return options;
}

/// <summary>
/// Apply F1 23-specific defaults. Used by test discovery.
/// </summary>
private static new FileWatcherOptions ApplyDefaults(FileWatcherOptions options)
{
return F1RaceReplaySourceBase.ApplyDefaults(EnsurePath(options));
}
}
}
47 changes: 47 additions & 0 deletions GamesDat/Telemetry/Sources/Formula1/F12024RaceReplaySource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace GamesDat.Core.Telemetry.Sources.Formula1
{
/// <summary>
/// File watcher source for F1 2024 race replay files.
/// Monitors: Documents\My Games\F1 24\replays
/// </summary>
public class F12024RaceReplaySource : F1RaceReplaySourceBase
{
public F12024RaceReplaySource(FileWatcherOptions options)
: base(ApplyDefaults(options))
{
}

public F12024RaceReplaySource(string? customPath = null)
: base(CreateDefaultOptions(customPath ?? GetDefaultReplayPath()))
{
}

public static string GetDefaultReplayPath() => GetReplayPathForYear("24");

/// <summary>
/// Ensure options has a path set, defaulting to F1 24 if not provided
/// </summary>
private static FileWatcherOptions EnsurePath(FileWatcherOptions options)
{
if (string.IsNullOrEmpty(options.Path))
{
return new FileWatcherOptions
{
Path = GetDefaultReplayPath(),
Patterns = options.Patterns,
IncludeSubdirectories = options.IncludeSubdirectories,
DebounceDelay = options.DebounceDelay
};
}
return options;
}

/// <summary>
/// Apply F1 24-specific defaults. Used by test discovery.
/// </summary>
private static new FileWatcherOptions ApplyDefaults(FileWatcherOptions options)
{
return F1RaceReplaySourceBase.ApplyDefaults(EnsurePath(options));
}
}
}
47 changes: 47 additions & 0 deletions GamesDat/Telemetry/Sources/Formula1/F12025RaceReplaySource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace GamesDat.Core.Telemetry.Sources.Formula1
{
/// <summary>
/// File watcher source for F1 2025 race replay files.
/// Monitors: Documents\My Games\F1 25\replays
/// </summary>
public class F12025RaceReplaySource : F1RaceReplaySourceBase
{
public F12025RaceReplaySource(FileWatcherOptions options)
: base(ApplyDefaults(options))
{
}

public F12025RaceReplaySource(string? customPath = null)
: base(CreateDefaultOptions(customPath ?? GetDefaultReplayPath()))
{
}

public static string GetDefaultReplayPath() => GetReplayPathForYear("25");

/// <summary>
/// Ensure options has a path set, defaulting to F1 25 if not provided
/// </summary>
private static FileWatcherOptions EnsurePath(FileWatcherOptions options)
{
if (string.IsNullOrEmpty(options.Path))
{
return new FileWatcherOptions
{
Path = GetDefaultReplayPath(),
Patterns = options.Patterns,
IncludeSubdirectories = options.IncludeSubdirectories,
DebounceDelay = options.DebounceDelay
};
}
return options;
}

/// <summary>
/// Apply F1 25-specific defaults. Used by test discovery.
/// </summary>
private static new FileWatcherOptions ApplyDefaults(FileWatcherOptions options)
{
return F1RaceReplaySourceBase.ApplyDefaults(EnsurePath(options));
}
}
}
93 changes: 93 additions & 0 deletions GamesDat/Telemetry/Sources/Formula1/F1RaceReplaySource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;
using System.IO;
using System.Linq;

namespace GamesDat.Core.Telemetry.Sources.Formula1
{
/// <summary>
/// File watcher source for F1 race replay files.
/// Defaults to F1 25, but can auto-detect the latest installed F1 game.
/// For monitoring specific years or multiple years simultaneously, use year-specific sources:
/// F12025RaceReplaySource, F12024RaceReplaySource, F12023RaceReplaySource, F12022RaceReplaySource
/// </summary>
public class F1RaceReplaySource : F1RaceReplaySourceBase
{
public F1RaceReplaySource(FileWatcherOptions options)
: base(ApplyDefaults(options))
{
}

/// <summary>
/// Ensure options has a path set, defaulting to F1 25 if not provided
/// </summary>
private static FileWatcherOptions EnsurePath(FileWatcherOptions options)
{
if (string.IsNullOrEmpty(options.Path))
{
return new FileWatcherOptions
{
Path = F12025RaceReplaySource.GetDefaultReplayPath(),
Patterns = options.Patterns,
IncludeSubdirectories = options.IncludeSubdirectories,
DebounceDelay = options.DebounceDelay
};
}
return options;
}

/// <summary>
/// Apply F1 defaults. Used by test discovery.
/// </summary>
private static new FileWatcherOptions ApplyDefaults(FileWatcherOptions options)
{
return F1RaceReplaySourceBase.ApplyDefaults(EnsurePath(options));
}

/// <summary>
/// Create F1 replay source with optional custom path and detection strategy
/// </summary>
/// <param name="customPath">Custom replay folder path. If provided, autoDetectLatestInstalled is ignored.</param>
/// <param name="autoDetectLatestInstalled">If true and customPath is null, auto-detects the latest installed F1 game (checks 25, 24, 23, 22). If false, defaults to F1 25.</param>
public F1RaceReplaySource(string? customPath = null, bool autoDetectLatestInstalled = false)
: base(CreateDefaultOptions(ResolveReplayPath(customPath, autoDetectLatestInstalled)))
{
}

/// <summary>
/// Get the default F1 replay folder path (F1 25)
/// </summary>
public static string GetDefaultReplayPath() => F12025RaceReplaySource.GetDefaultReplayPath();

/// <summary>
/// Auto-detect the latest installed F1 game's replay folder.
/// Checks F1 25, F1 24, F1 23, F1 22 in order and returns the first that exists.
/// Note: This checks for folder existence, which may include cases where the game
/// is uninstalled but replay folders remain.
/// </summary>
public static string GetLatestInstalledReplayPath()
{
var years = new[] { "25", "24", "23", "22" };
var existingPath = years
.Select(GetReplayPathForYear)
.FirstOrDefault(Directory.Exists);

// Default to F1 25 even if it doesn't exist
return existingPath ?? GetReplayPathForYear("25");
}

private static string ResolveReplayPath(string? customPath, bool autoDetectLatestInstalled)
{
if (customPath != null)
{
return customPath;
}

if (autoDetectLatestInstalled)
{
return GetLatestInstalledReplayPath();
}

return GetDefaultReplayPath();
}
}
}
77 changes: 77 additions & 0 deletions GamesDat/Telemetry/Sources/Formula1/F1RaceReplaySourceBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;

namespace GamesDat.Core.Telemetry.Sources.Formula1
{
/// <summary>
/// Abstract base class for F1 race replay file sources across different game years.
/// Provides common functionality for monitoring F1 replay directories.
/// </summary>
public abstract class F1RaceReplaySourceBase : FileWatcherSourceBase
{
/// <summary>
/// The default debounce delay used by FileWatcherOptions (1 second).
/// Note: This mirrors the default in FileWatcherOptions.DebounceDelay.
/// If that default changes, this constant should be updated to match.
/// </summary>
private static readonly TimeSpan LibraryDefaultDebounceDelay = TimeSpan.FromSeconds(1);

/// <summary>
/// The F1-specific debounce delay (2 seconds)
/// </summary>
private static readonly TimeSpan F1DebounceDelay = TimeSpan.FromSeconds(2);

protected F1RaceReplaySourceBase(FileWatcherOptions options) : base(options)
{
}

/// <summary>
/// Build the full replay path for a specific F1 game year
/// </summary>
protected static string GetReplayPathForYear(string gameYear)
{
var documentsFolder = Environment.GetFolderPath(
Environment.SpecialFolder.MyDocuments,
Environment.SpecialFolderOption.DoNotVerify);
return System.IO.Path.Combine(documentsFolder, "My Games", $"F1 {gameYear}", "replays");
}

/// <summary>
/// Create default FileWatcherOptions for F1 replay monitoring
/// </summary>
protected static FileWatcherOptions CreateDefaultOptions(string path)
{
return new FileWatcherOptions
{
Path = path,
Patterns = new[] { "*.frr" },
IncludeSubdirectories = false,
DebounceDelay = F1DebounceDelay
};
}

/// <summary>
/// Apply F1-specific defaults to options. Requires Path to be non-empty.
/// </summary>
protected static FileWatcherOptions ApplyDefaults(FileWatcherOptions options)
{
if (string.IsNullOrEmpty(options.Path))
{
throw new ArgumentException("FileWatcherOptions.Path cannot be null or empty. Provide a path before calling ApplyDefaults.", nameof(options));
}

return new FileWatcherOptions
{
Path = options.Path,
Patterns = options.Patterns == null || options.Patterns.Length == 0
? new[] { "*.frr" }
: options.Patterns,
IncludeSubdirectories = options.IncludeSubdirectories,
// Check for both default (00:00:00, used when constructed via string path)
// and LibraryDefaultDebounceDelay (1s, used when constructed via options without explicit delay)
DebounceDelay = options.DebounceDelay == default || options.DebounceDelay == LibraryDefaultDebounceDelay
? F1DebounceDelay
: options.DebounceDelay
};
}
}
}
Loading