Skip to content
Draft
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
6 changes: 5 additions & 1 deletion GenHub/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.2.7" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.7" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageVersion Include="CsvHelper" Version="30.0.1" />
<PackageVersion Include="Material.Icons.Avalonia" Version="2.4.1" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
Expand All @@ -23,6 +26,7 @@
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="9.0.0" />
<PackageVersion Include="Microsoft.Playwright" Version="1.55.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="ReactiveUI" Version="19.6.1" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.1.118" />
<!-- Test packages -->
Expand All @@ -34,4 +38,4 @@
<PackageVersion Include="moq" Version="4.20.72" />
<PackageVersion Include="FluentAssertions" Version="6.12.1" />
</ItemGroup>
</Project>
</Project>
45 changes: 21 additions & 24 deletions GenHub/GenHub.Core/Constants/ConfigurationKeys.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
namespace GenHub.Core.Constants;

/// <summary>
/// Configuration key constants for appsettings.json and environment variables.
/// </summary>
Expand All @@ -9,101 +8,99 @@ public static class ConfigurationKeys
/// Base configuration section for GenHub settings.
/// </summary>
public const string GenHubSection = "GenHub";

// Workspace configuration keys

/// <summary>
/// Configuration key for default workspace path.
/// </summary>
public const string WorkspaceDefaultPath = "GenHub:Workspace:DefaultPath";

/// <summary>
/// Configuration key for default workspace strategy.
/// </summary>
public const string WorkspaceDefaultStrategy = "GenHub:Workspace:DefaultStrategy";

// Cache configuration keys

/// <summary>
/// Configuration key for default cache directory path.
/// </summary>
public const string CacheDefaultPath = "GenHub:Cache:DefaultPath";

// UI configuration keys

/// <summary>
/// Configuration key for default UI theme.
/// </summary>
public const string UiDefaultTheme = "GenHub:UI:DefaultTheme";

/// <summary>
/// Configuration key for default window width.
/// </summary>
public const string UiDefaultWindowWidth = "GenHub:UI:DefaultWindowWidth";

/// <summary>
/// Configuration key for default window height.
/// </summary>
public const string UiDefaultWindowHeight = "GenHub:UI:DefaultWindowHeight";

// Downloads configuration keys

/// <summary>
/// Configuration key for default download timeout in seconds.
/// </summary>
public const string DownloadsDefaultTimeoutSeconds = "GenHub:Downloads:DefaultTimeoutSeconds";

/// <summary>
/// Configuration key for default user agent string.
/// </summary>
public const string DownloadsDefaultUserAgent = "GenHub:Downloads:DefaultUserAgent";

/// <summary>
/// Configuration key for default maximum concurrent downloads.
/// </summary>
public const string DownloadsDefaultMaxConcurrent = "GenHub:Downloads:DefaultMaxConcurrent";

/// <summary>
/// Configuration key for default download buffer size.
/// </summary>
public const string DownloadsDefaultBufferSize = "GenHub:Downloads:DefaultBufferSize";

// Downloads policy configuration keys

/// <summary>
/// Configuration key for minimum concurrent downloads policy.
/// </summary>
public const string DownloadsPolicyMinConcurrent = "GenHub:Downloads:Policy:MinConcurrent";

/// <summary>
/// Configuration key for maximum concurrent downloads policy.
/// </summary>
public const string DownloadsPolicyMaxConcurrent = "GenHub:Downloads:Policy:MaxConcurrent";

/// <summary>
/// Configuration key for minimum download timeout policy.
/// </summary>
public const string DownloadsPolicyMinTimeoutSeconds = "GenHub:Downloads:Policy:MinTimeoutSeconds";

/// <summary>
/// Configuration key for maximum download timeout policy.
/// </summary>
public const string DownloadsPolicyMaxTimeoutSeconds = "GenHub:Downloads:Policy:MaxTimeoutSeconds";

/// <summary>
/// Configuration key for minimum download buffer size policy.
/// </summary>
public const string DownloadsPolicyMinBufferSizeBytes = "GenHub:Downloads:Policy:MinBufferSizeBytes";

/// <summary>
/// Configuration key for maximum download buffer size policy.
/// </summary>
public const string DownloadsPolicyMaxBufferSizeBytes = "GenHub:Downloads:Policy:MaxBufferSizeBytes";

// App data configuration key

/// <summary>
/// Configuration key for application data path.
/// </summary>
public const string AppDataPath = "GenHub:AppDataPath";
// CSV configuration keys
/// <summary>
/// Configuration key for CSV index URL.
/// </summary>
public const string CsvIndexUrl = "GenHub:CSV:IndexUrl";
/// <summary>
/// Configuration key for CSV catalogs fallback.
/// </summary>
public const string CsvCatalogs = "GenHub:CSV:Catalogs";
/// <summary>
/// Configuration key for auto-detect language flag.
/// </summary>
public const string CsvAutoDetectLanguage = "GenHub:CSV:AutoDetectLanguage";
/// <summary>
/// Configuration key for CSV cache TTL in minutes.
/// </summary>
public const string CsvCacheTtlMinutes = "GenHub:CSV:CacheTtlMinutes";
/// <summary>
/// Configuration key for CSV strict mode.
/// </summary>
public const string CsvStrictMode = "GenHub:CSV:StrictMode";
}
151 changes: 151 additions & 0 deletions GenHub/GenHub.Core/Constants/CsvConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
using System;

namespace GenHub.Core.Constants;

/// <summary>Constants used for CSV file processing and content handling.</summary>
public static class CsvConstants
{
/// <summary>CSV file extension.</summary>
public const string CsvFileExtension = ".csv";

/// <summary>CSV file pattern for file matching.</summary>
public const string CsvFilePattern = "*.csv";

/// <summary>Default CSV delimiter character.</summary>
public const char CsvDelimiter = ',';

/// <summary>Maximum number of columns expected in CSV files.</summary>
public const int MaxCsvColumns = 8;

/// <summary>Minimum number of columns required in CSV files.</summary>
public const int MinCsvColumns = 4;

/// <summary>Column index for relative path in CSV files (0-based).</summary>
public const int RelativePathColumnIndex = 0;

/// <summary>Column index for file size in CSV files (0-based).</summary>
public const int SizeColumnIndex = 1;

/// <summary>Column index for MD5 hash in CSV files (0-based).</summary>
public const int Md5ColumnIndex = 2;

/// <summary>Column index for SHA256 hash in CSV files (0-based).</summary>
public const int Sha256ColumnIndex = 3;

/// <summary>Column index for game type in CSV files (0-based).</summary>
public const int GameTypeColumnIndex = 4;

/// <summary>Column index for language in CSV files (0-based).</summary>
public const int LanguageColumnIndex = 5;

/// <summary>Column index for isRequired in CSV files (0-based).</summary>
public const int IsRequiredColumnIndex = 6;

/// <summary>Column index for metadata in CSV files (0-based).</summary>
public const int MetadataColumnIndex = 7;

/// <summary>Default buffer size for CSV file reading operations.</summary>
public const int DefaultCsvBufferSize = 8192;

/// <summary>Timeout for CSV download operations in seconds.</summary>
public const int CsvDownloadTimeoutSeconds = 30;

/// <summary>Maximum retry attempts for CSV operations.</summary>
public const int MaxCsvRetryAttempts = 3;

/// <summary>Delay between CSV retry attempts in milliseconds.</summary>
public const int CsvRetryDelayMs = 1000;

/// <summary>Default CSV URL for Generals content.</summary>
public const string DefaultGeneralsCsvUrl = "https://raw.githubusercontent.com/Community-Outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv";

/// <summary>Default CSV URL for Zero Hour content.</summary>
public const string DefaultZeroHourCsvUrl = "https://raw.githubusercontent.com/Community-Outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv";

/// <summary>Default index.json URL for CSV registry metadata.</summary>
public const string DefaultCsvIndexUrl = "https://raw.githubusercontent.com/Community-Outpost/GenHub/main/docs/GameInstallationFilesRegistry/index.json";

/// <summary>CSV resolver identifier.</summary>
public const string CsvResolverId = "CSVResolver";

/// <summary>CSV source name identifier.</summary>
public const string CsvSourceName = "CSV";

/// <summary>File system deliverer source name.</summary>
public const string FileSystemSourceName = "FileSystem";

/// <summary>Default hash algorithm to use for file validation.</summary>
public const string DefaultHashAlgorithm = "SHA256";

/// <summary>Progress message for CSV download phase.</summary>
public const string DownloadingCsvMessage = "Downloading CSV file...";

/// <summary>Progress message for CSV parsing and validation phase.</summary>
public const string ParsingCsvMessage = "Parsing CSV and validating files...";

/// <summary>Error message when CSV URL is not provided.</summary>
public const string CsvUrlNotProvidedError = "CSV URL not provided by discoverer";

/// <summary>Error message when CSV download fails.</summary>
public const string CsvDownloadFailedError = "Failed to download CSV";

/// <summary>Error message when content is not found.</summary>
public const string ContentNotFoundError = "Content not found";

/// <summary>Error message when manifest is not available.</summary>
public const string ManifestNotAvailableError = "Manifest not available in search result";

/// <summary>Error message for operation cancellation.</summary>
public const string OperationCancelledError = "Operation canceled";

/// <summary>Error message for CSV resolve failure.</summary>
public const string CsvResolveFailedError = "CSV resolve failed";

/// <summary>Error message for CSV content preparation failure.</summary>
public const string CsvPreparationFailedError = "CSV content preparation failed";

/// <summary>Warning message for malformed CSV lines.</summary>
public const string MalformedCsvLineWarning = "CSVDeliverer: skipping malformed CSV line {LineIndex} (not enough columns)";

/// <summary>Warning message for empty relative paths.</summary>
public const string EmptyRelativePathWarning = "CSVDeliverer: skipping empty relPath at line {LineIndex}";

/// <summary>Warning message for size parse failures.</summary>
public const string SizeParseFailedWarning = "CSVDeliverer: skipping {RelPath} because size parse failed at line {LineIndex}";

/// <summary>Warning message for MD5 mismatches.</summary>
public const string Md5MismatchWarning = "MD5 mismatch: {RelativePath}";

/// <summary>Warning message for SHA256 mismatches.</summary>
public const string Sha256MismatchWarning = "SHA256 mismatch: {RelativePath}";

/// <summary>Warning message for missing files.</summary>
public const string FileMissingWarning = "File missing: {RelativePath}";

/// <summary>Warning message for invalid file sizes.</summary>
public const string InvalidSizeWarning = "Invalid size for file {RelativePath}";

/// <summary>Debug message for starting CSV preparation.</summary>
public const string StartingCsvPreparationDebug = "Starting CSV content preparation for manifest {ManifestId}";

/// <summary>Debug message for successful CSV preparation completion.</summary>
public const string CsvPreparationCompletedDebug = "CSV content preparation completed successfully for manifest {ManifestId}";

/// <summary>Warning message for CSV validation errors.</summary>
public const string CsvValidationErrorsWarning = "CSV validation completed with errors: {Errors}";

/// <summary>Error message for CSV download failures.</summary>
public const string CsvDownloadFailedLog = "CSVDeliverer: failed to download CSV from {Url} - status {Status}";

/// <summary>Error message for missing CSV URL.</summary>
public const string CsvUrlMissingLog = "CSVDeliverer: csvUrl not provided in discovered item (Id={ContentId})";

/// <summary>Warning message for operation cancellation.</summary>
public const string OperationCancelledLog = "CSVDeliverer: operation was cancelled";

/// <summary>Error message for CSV resolve failures.</summary>
public const string CsvResolveFailedLog = "CSVDeliverer: failed to resolve CSV discovered item (Id={ContentId})";

/// <summary>Error message for CSV preparation failures.</summary>
public const string CsvPreparationFailedLog = "CSV content preparation failed for manifest {ManifestId}";
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ public static GameInstallation ToDomain(this IGameInstallation installation, ILo
{
logger?.LogDebug("Converting {InstallationType} installation to domain model", installation.InstallationType);

var installationPath = installation.HasGenerals ? installation.GeneralsPath : installation.ZeroHourPath;
if (string.IsNullOrEmpty(installationPath))
var gameInstallation = new GameInstallation(installation.InstallationPath, installation.InstallationType, logger as ILogger<GameInstallation>)
{
installationPath = installation.InstallationPath;
}

var gameInstallation = new GameInstallation(installationPath, installation.InstallationType, logger as ILogger<GameInstallation>);
HasGenerals = installation.HasGenerals,
GeneralsPath = installation.GeneralsPath,
HasZeroHour = installation.HasZeroHour,
ZeroHourPath = installation.ZeroHourPath,
};

logger?.LogDebug("Successfully converted installation to domain model: {InstallationPath}", installationPath);
logger?.LogDebug("Successfully converted installation to domain model: {InstallationPath}", installation.InstallationPath);
return gameInstallation;
}

Expand Down
18 changes: 18 additions & 0 deletions GenHub/GenHub.Core/Features/GameInstallations/ILanguageDetector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Threading;
using System.Threading.Tasks;

namespace GenHub.Core.Features.GameInstallations;

/// <summary>
/// Interface for detecting the language of a game installation.
/// </summary>
public interface ILanguageDetector
{
/// <summary>
/// Detects the language of a game installation at the specified path.
/// </summary>
/// <param name="installationPath">The path to the game installation directory.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>The detected language code in uppercase (e.g., "EN", "DE"), or "EN" as fallback.</returns>
Task<string> DetectAsync(string installationPath, CancellationToken cancellationToken = default);
}
Loading