The Validation System ensures that game installations, content packages, and assembled workspaces are complete and safe to use. It employs a multi-level integrity check strategy, distinguishing between critical failures (missing files) and non-critical warnings (extraneous files).
Architecture
The validation system is built on a specific Result Pattern that allows for granular issue tracking rather than simple boolean pass/fail.
Core Components
| Component | Interface | Responsibility |
|---|---|---|
| ContentValidator | IContentValidator | Validates a folder against a ContentManifest. Checks file existence, hashes, and extra files. |
| GameInstallationValidator | IGameInstallationValidator | Specialized wrapper for base games. Orchestrates manifest retrieval and directory validation. |
| FileSystemValidator | Base Class | Provides shared logic for file existence and hash verification. |
| ValidationResult | Model | Aggregates a list of ValidationIssue objects and determines overall success. |
The Validation Logic
1. Structure Validation
Checks if the ContentManifest itself is valid.
- Critical Errors: Missing
Id,Fileslist is null/empty. - Rules: IDs must match the Manifest ID Schema (e.g.,
1.87.swr.mod.rotr).
2. Content Integrity
Verifies that the files on disk match the manifest.
- Existence: Every file listed in
Filesmust exist. (Error) - Content Addressable Storage (CAS): If source type is
ContentAddressable, verifies the hash exists in the CAS index. - Hash Verification:
- Calculates SHA256 hash of on-disk files.
- Compares against
manifest.Files[i].Hash. - Behavior: Currently, hash mismatches are treated as Warnings rather than Errors in some contexts to allow for minor user modifications (like config tweaks) without breaking the game.
3. Extraneous File Detection
Scans the target directory for files not in the manifest.
- Purpose: Essential for keeping game folders clean, especially when using symbolic links.
- Behavior:
- Creates a
HashSetof all expected file paths. - Recursively scans the directory.
- Any file not in the set is flagged.
- Severity: Warning. Use these warnings to suggest a "Cleanup" action to the user.
- Creates a
The Result Pattern
Validation does not throw exceptions for validity failures; it returns a structured result object.
public class ValidationResult : ResultBase
{
public bool IsValid => !Issues.Any(i => i.Severity == ValidationSeverity.Error);
public IReadOnlyList<ValidationIssue> Issues { get; }
}
public class ValidationIssue
{
public ValidationSeverity Severity { get; } // Info, Warning, Error, Critical
public string Message { get; }
public string Path { get; }
}Success Logic
The DetermineSuccess method defines that a result is Success (Valid) if there are zero issues with Severity >= Error.
- Success: 0 Issues.
- Success: 5 Warnings (e.g., "Extraneous file: dirty_map.map").
- Failure: 1 Error (e.g., "Missing file: Data/generals.ctr").
Usage Flow
Automatic Validation
Validation is triggered automatically in these key workflows:
- Import: When adding new content, it is fully validated before being registered in the pool.
- Game Detection: When a new game installation is detected,
GameInstallationValidatorensures it isn't corrupted. - Pre-Launch: A "Flight Check" runs quickly before launching to ensure no files were deleted since the last play session.
Performance
To handle large mods (GBs of data):
- Parallel Processing:
ValidateContentIntegrityAsyncusesSemaphoreSlimto hash files in parallel (up to logical processor count). - Progress Reporting: All methods accept
IProgress<ValidationProgress>to drive UI progress bars.
