### StandardInstaller.Begin — Install a Modlist Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Execute the complete installation pipeline from a .wabbajack file. This includes hashing, downloading, extracting, applying patches, and configuring game files. Progress and user confirmations can be handled via callbacks. ```csharp using Wabbajack.Installer; using Wabbajack.DTOs; using Microsoft.Extensions.DependencyInjection; // Load the ModList metadata from the .wabbajack zip var dtos = services.GetRequiredService(); var modlist = await StandardInstaller.LoadFromFile(dtos, (AbsolutePath)@"C:\\Downloads\\MyList.wabbajack"); var config = new InstallerConfiguration { ModlistArchive = (AbsolutePath)@"C:\\Downloads\\MyList.wabbajack", ModList = modlist, Install = (AbsolutePath)@"C:\\Games\\SkyrimSE_MyList", Downloads = (AbsolutePath)@"C:\\Games\\SkyrimSE_MyList\\downloads", Game = modlist.GameType, // GameFolder is auto-located if not set }; var installer = StandardInstaller.Create(services, config); // Track progress installer.OnStatusUpdate = update => Console.WriteLine($"[{update.StatusCategory}] {update.StatusText} " + $"{update.StepsProgress.Value * 100:F0}%"); // Optionally confirm file deletions interactively installer.OnConfirmAction = async (title, message) => { Console.WriteLine($"{title}\n{message}"); return Console.ReadLine()?.Trim().ToLower() == "y"; }; using var cts = new CancellationTokenSource(); var result = await installer.Begin(cts.Token); switch (result) { case InstallResult.Succeeded: Console.WriteLine("Installation complete!"); break; case InstallResult.DownloadFailed: Console.Error.WriteLine("One or more archives could not be downloaded."); break; case InstallResult.GameMissing: Console.Error.WriteLine("Target game not found on this machine."); break; case InstallResult.Cancelled: Console.WriteLine("Installation cancelled by user."); break; } // Installation phases (in order): // 1. OptimizeModlist – skip already-correct files // 2. HashArchives – hash existing downloads and game files // 3. DownloadArchives – download missing files (skips Manual ones first) // 4. HashArchives – re-hash to confirm downloads // 5. ExtractModlist – unzip .wabbajack into temp folder // 6. PrimeVFS – register archive hash paths without full extraction // 7. BuildFolderStructure – create destination folder tree // 8. InstallArchives – extract/patch/recompress all FromArchive directives // 9. InstallIncludedFiles – write InlineFile / RemappedInlineFile directives // 10. WriteMetaFiles – write .meta sidecars for downloads folder // 11. BuildBSAs – assemble BSA archives from extracted components // 12. GenerateZEditMerges – reconstruct zEdit merged patches // 13. ForcePortable – write portable.txt // 14. RemapMO2File – fix ModOrganizer.ini download_directory path ``` -------------------------------- ### Install a Modlist using wabbajack-cli Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Installs a modlist from a .wabbajack file or a machine URL. Specify the output directory and the downloads directory. ```bash # Install a modlist from a .wabbajack file wabbajack-cli install \ --wabbajack "C:\Downloads\MyList.wabbajack" \ --output "C:\Games\SkyrimSE_MyList" \ --downloads "C:\Games\SkyrimSE_MyList\downloads" # Install directly from a machine URL (downloads the .wabbajack first) wabbajack-cli install \ --machineUrl "wj-featured/living-skyrim-4" \ --wabbajack "C:\Temp\ls4.wabbajack" \ --output "C:\Games\LS4" \ --downloads "C:\Games\LS4\downloads" ``` -------------------------------- ### Implement Custom Compilation Step with ICompilationStep Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Extend ACompilationStep to create custom file handling logic. Return a Directive to claim the file or null to pass it on. This example inlines 'custom_settings.ini' files. ```csharp using Wabbajack.Compiler; using Wabbajack.Compiler.CompilationSteps; using Wabbajack.DTOs.Directives; // A step that inlines any file named "custom_settings.ini" directly into the .wabbajack public class IncludeCustomSettings : ACompilationStep { public IncludeCustomSettings(ACompiler compiler) : base(compiler) { } public override async ValueTask Run(RawSourceFile source) { if (source.Path.FileName.ToString() != "custom_settings.ini") return null; // not our file – pass to next step // InlineFile embeds the file's data inside the .wabbajack archive var inlined = source.EvolveTo(); inlined.SourceDataID = await _compiler.IncludeFile(source.AbsolutePath, token: default); return inlined; } } // Register in the stack before DropAll: // In MO2Compiler.MakeStack() add: new IncludeCustomSettings(this), ``` -------------------------------- ### Auto-detect Compiler Settings from MO2 Installation Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Automatically infer compiler settings by reading an MO2 installation's `ModOrganizer.ini` and profile `modlist.txt`. This simplifies setup by detecting game, download paths, include/ignore tags, and additional profiles. Ensure the provided path points to the MO2 root directory. ```csharp using Wabbajack.Compiler; using Wabbajack.Paths; using Microsoft.Extensions.Logging; // Resolve via DI in practice; shown inline for clarity var logger = LoggerFactory.Create(b => b.AddConsole()) .CreateLogger(); var inferencer = new CompilerSettingsInferencer(logger); // Point at the MO2 root (folder containing ModOrganizer.ini) var mo2Root = (AbsolutePath)@"C:\MO2\SkyrimSE"; var settings = await inferencer.InferFromRootPath(mo2Root); if (settings == null) { Console.Error.WriteLine("Could not infer settings – is this an MO2 install?"); return 1; } // Override auto-detected values if needed settings.ModListName = "My Released List"; settings.ModListAuthor = "MyNexusName"; settings.OutputFile = (AbsolutePath)@"C:\Releases\MyList.wabbajack"; settings.UseGamePaths = true; settings.AutoGenerateReport = true; Console.WriteLine($"Detected game: {settings.Game}"); Console.WriteLine($"Source: {settings.Source}"); Console.WriteLine($"Downloads: {settings.Downloads}"); Console.WriteLine($"Profile: {settings.Profile}"); ``` -------------------------------- ### Compile a Modlist using wabbajack-cli Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Compiles a modlist from a Mod Organizer 2 installation directory to a specified output path. ```bash # Compile a modlist from an MO2 installation wabbajack-cli compile \ --installPath "C:\MO2\SkyrimSE" \ --outputPath "C:\Releases" ``` -------------------------------- ### Run Full Modlist Compilation with MO2Compiler.Begin Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Initiates the complete modlist compilation process, including VFS indexing, archive resolution, compilation stack execution, binary patching, BSA deconstruction, and `.wabbajack` archive export. Subscribe to `OnStatusUpdate` for progress feedback. Returns `true` on success. ```csharp using Wabbajack.Compiler; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; // Build the DI container (typically done once at app startup) var services = new ServiceCollection() .AddLogging(b => b.AddConsole()) .AddSingleton(settings) // settings from above .AddWabbajackCompiler() // extension from Wabbajack.Services.OSIntegrated .BuildServiceProvider(); // Create and run the compiler var compiler = MO2Compiler.Create(services, settings); // Subscribe to step-by-step progress compiler.OnStatusUpdate += (_, update) => { Console.WriteLine($"[{update.Category}] {update.StatusText} " + $ ``` -------------------------------- ### Implement Custom Downloader with IDownloader Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Extend IDownloader to add new download sources. Register your implementation in DI. Ensure your custom state implements IDownloadState. ```csharp using Wabbajack.Downloaders.Interfaces; using Wabbajack.DTOs; using Wabbajack.DTOs.DownloadStates; using Wabbajack.DTOs.Validation; using Wabbajack.Hashing.xxHash64; using Wabbajack.Paths; using Wabbajack.RateLimiter; // Custom download state public record MyCustomState : IDownloadState { public string Url { get; init; } = ""; public string PrimaryKeyString => Url; public IDownloadState? ConvertToChunkedSeekableState() => null; public bool IsWhitelisted(ServerAllowList allowList) => true; } public class MyCustomDownloader : IDownloader { public Priority Priority => Priority.Normal; public bool CanDownload(Archive archive) => archive.State is MyCustomState; public async Task Download(Archive archive, AbsolutePath destination, IJob job, CancellationToken token) { var state = (MyCustomState)archive.State; using var http = new HttpClient(); await using var stream = await http.GetStreamAsync(state.Url, token); return await destination.WriteAllHashedAsync(stream, token); } public async Task Download(Archive archive, MyCustomState state, AbsolutePath destination, IJob job, CancellationToken token) => await Download(archive, destination, job, token); public async Task Verify(Archive archive, IJob job, CancellationToken token) { var state = (MyCustomState)archive.State; using var http = new HttpClient(); var resp = await http.SendAsync( new HttpRequestMessage(HttpMethod.Head, state.Url), token); return resp.IsSuccessStatusCode; } public Task Prepare() => Task.FromResult(true); public bool IsAllowed(ServerAllowList allowList, IDownloadState state) => true; public IEnumerable MetaIni(Archive a) { var s = (MyCustomState)a.State; yield return $"directURL={s.Url}"; } public IDownloadState? Resolve(IReadOnlyDictionary iniData) => iniData.TryGetValue("directURL", out var url) ? new MyCustomState { Url = url } : null; } ``` -------------------------------- ### Resource Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Controls concurrency and throughput for any category of parallel work (downloading, hashing, compiling, installing). Each category has its own named `Resource` with configurable max tasks and max bytes-per-second throughput. ```APIDOC ## Resource — Parallel Job Rate Limiter `Resource` controls concurrency and throughput for any category of parallel work (downloading, hashing, compiling, installing). Each category has its own named `Resource` with configurable max tasks and max bytes-per-second throughput. ```csharp using Wabbajack.RateLimiter; // Create a resource allowing 4 concurrent tasks with 10 MB/s throughput cap var downloadLimiter = new Resource( humanName: "Downloads", maxTasks: 4, maxThroughput: 10 * 1024 * 1024); // 10 MB/s // Acquire a slot before starting work using var job = await downloadLimiter.Begin("Downloading SomeMod.zip", fileSize, token); // Report bytes processed (throttles automatically to stay within maxThroughput) await job.Report(bytesProcessed, token); // Or report without waiting for throttle (fire and forget) downloadLimiter.ReportNoWait(job, bytesProcessed); // job.Dispose() releases the semaphore slot // Inspect current activity var status = downloadLimiter.StatusReport; Console.WriteLine($"Active: {status.Running}, Waiting: {status.Pending}, " + $"Total bytes: {status.Transferred}"); // Create a resource from dynamic settings (e.g. user preferences) var hashLimiter = new Resource( humanName: "File Hashing", settingGetter: async () => { var prefs = await LoadUserPreferences(); return (prefs.MaxHashThreads, long.MaxValue); // no throughput limit }); ``` ``` -------------------------------- ### List Games using wabbajack-cli Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Lists all supported games. ```bash # List all supported games wabbajack-cli list-games ``` -------------------------------- ### List Modlists using wabbajack-cli Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Lists all available modlists. ```bash # List all available modlists wabbajack-cli list-modlists ``` -------------------------------- ### Manage Parallel Job Concurrency with Resource Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Resource limits concurrent tasks and throughput for categories like downloading or hashing. Configure max tasks and bytes-per-second. Acquire a slot using Begin() before starting work and report progress with Report() or ReportNoWait(). Dispose the job to release the slot. ```csharp using Wabbajack.RateLimiter; // Create a resource allowing 4 concurrent tasks with 10 MB/s throughput cap var downloadLimiter = new Resource( humanName: "Downloads", maxTasks: 4, maxThroughput: 10 * 1024 * 1024); // 10 MB/s // Acquire a slot before starting work using var job = await downloadLimiter.Begin("Downloading SomeMod.zip", fileSize, token); // Report bytes processed (throttles automatically to stay within maxThroughput) await job.Report(bytesProcessed, token); // Or report without waiting for throttle (fire and forget) downloadLimiter.ReportNoWait(job, bytesProcessed); // job.Dispose() releases the semaphore slot // Inspect current activity var status = downloadLimiter.StatusReport; Console.WriteLine($"Active: {status.Running}, Waiting: {status.Pending}, " + $ ``` -------------------------------- ### Load .wabbajack file manifest and extract cover image Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt C# code demonstrating how to load a .wabbajack file's manifest using `StandardInstaller.LoadFromFile` and extract its cover image using `StandardInstaller.ModListImageStream`. Requires `DTOSerializer` and `Wabbajack.Paths`. ```csharp using Wabbajack.Installer; using Wabbajack.DTOs.JsonConverters; using Wabbajack.Paths; var dtos = services.GetRequiredService(); var wabbajackFile = (AbsolutePath)@"C:\Downloads\MyList.wabbajack"; // Load the ModList manifest var modList = await StandardInstaller.LoadFromFile(dtos, wabbajackFile); Console.WriteLine($"Name: {modList.Name}"); Console.WriteLine($"Author: {modList.Author}"); Console.WriteLine($"Game: {modList.GameType}"); Console.WriteLine($"Version: {modList.Version}"); Console.WriteLine($"Archives: {modList.Archives.Length}"); Console.WriteLine($"Directives:{modList.Directives.Length}"); Console.WriteLine($"NSFW: {modList.IsNSFW}"); // Extract the cover image (returns null if no image in the archive) await using var imageStream = await StandardInstaller.ModListImageStream(wabbajackFile); if (imageStream != null) { await using var outFile = File.Create(@"C:\Temp\cover.png"); await imageStream.CopyToAsync(outFile); } // Load a ModList directly from a remote ModlistMetadata entry var modListFromRemote = await StandardInstaller.Load( dtos, dispatcher, modlistMetadata, CancellationToken.None); ``` -------------------------------- ### Interact with Wabbajack Build Server using Client API Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Use the Client API to browse modlists, look up archives by hash, fetch game-specific stock archives, manage download allow-lists and mirrors, send telemetry, upload author files, and publish modlist versions. Requires authentication via WabbajackApiState. ```csharp using Wabbajack.Networking.WabbajackClientApi; // client is injected via DI // Browse all available modlists from all registered repositories var lists = await client.LoadLists(); foreach (var list in lists) { Console.WriteLine($"{list.NamespacedName} v{list.Version} " + $ ``` -------------------------------- ### Authenticate with Mega Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Use the `mega-login` command to authenticate with Mega using user credentials. ```bash wabbajack-cli mega-login --email user@example.com --password hunter2 ``` -------------------------------- ### wabbajack-cli Command Reference Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Exposes all core Wabbajack operations as command-line verbs. ```APIDOC ## CLI — wabbajack-cli Command Reference `wabbajack-cli` exposes all core Wabbajack operations as command-line verbs. The binary is built from `Wabbajack.CLI`. ```bash # Compile a modlist from an MO2 installation wabbajack-cli compile \ --installPath "C:\MO2\SkyrimSE" \ --outputPath "C:\Releases" # Install a modlist from a .wabbajack file wabbajack-cli install \ --wabbajack "C:\Downloads\MyList.wabbajack" \ --output "C:\Games\SkyrimSE_MyList" \ --downloads "C:\Games\SkyrimSE_MyList\downloads" # Install directly from a machine URL (downloads the .wabbajack first) wabbajack-cli install \ --machineUrl "wj-featured/living-skyrim-4" \ --wabbajack "C:\Temp\ls4.wabbajack" \ --output "C:\Games\LS4" \ --downloads "C:\Games\LS4\downloads" # Hash a single file using Wabbajack's xxHash64 wabbajack-cli hash-file --input "C:\BigMod.zip" # Output: C:\BigMod.zip hash: AAABBBCCCDDD 1234567890abcdef 9876543210 # Index (VFS-analyze) a folder and cache results wabbajack-cli vfs-index --folder "C:\MO2\SkyrimSE\downloads" # Validate all community modlists and generate status reports wabbajack-cli validate-lists --reports "C:\ValidationReports" # List all available modlists wabbajack-cli list-modlists # List all supported games wabbajack-cli list-games ``` ``` -------------------------------- ### Download a single URL using Wabbajack CLI Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Use the `download-url` command to download a file from a given URL to a specified output path. ```bash wabbajack-cli download-url --url "https://example.com/file.zip" \ --output "C:\downloads\file.zip" ``` -------------------------------- ### Index File System Roots with VFS Context Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Recursively analyzes files under given paths to build a hash-indexed tree of all files, including those nested in archives. Use `AddRoots` to index multiple directories and query the index by disk path or hash. Files can be extracted using the `Extract` method. ```csharp using Wabbajack.VFS; using Wabbajack.Paths; using Microsoft.Extensions.DependencyInjection; var vfs = services.GetRequiredService(); // Index a set of folder roots (runs in parallel) var roots = new[] { (AbsolutePath)@"C:\\MO2\\SkyrimSE", (AbsolutePath)@"C:\\MO2\\SkyrimSE\\downloads", (AbsolutePath)@"C:\\Steam\\steamapps\\common\\Skyrim Special Edition" }; await vfs.AddRoots(roots, CancellationToken.None, async (current, max) => Console.WriteLine($"Indexed {current}/{max} files")); // Query the index var index = vfs.Index; // Look up a file by its disk path if (index.ByRootPath.TryGetValue((AbsolutePath)@"C:\\MO2\\SkyrimSE\\mods\\SomeMod\\textures\\a.dds", out var vf)) { Console.WriteLine($"Hash: {vf.Hash.ToHex()}"); Console.WriteLine($"Size: {vf.Size}"); Console.WriteLine($"IsArchive: {vf.IsArchive}"); // true if it contains children Console.WriteLine($"NestingFactor: {vf.NestingFactor}"); // 1 = native file } // Look up all indexed files sharing a hash if (index.ByHash.TryGetValue(someHash, out var matching)) foreach (var file in matching) Console.WriteLine(file.FullPath); // Extract a set of specific virtual files to a callback var filesToExtract = new HashSet { vf }; await vfs.Extract(filesToExtract, async (virtualFile, extractedFile) => { await using var stream = await extractedFile.GetStream(); Console.WriteLine($"Extracted {virtualFile.Name}, size={{stream.Length}}"); }, CancellationToken.None); ``` -------------------------------- ### IDownloader - Implement a Custom Downloader Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Implement the `IDownloader` interface to add new download sources to Wabbajack. This involves defining how to handle custom download states, download archives, and verify their integrity. ```APIDOC ## IDownloader — Implement a Custom Downloader Implement `IDownloader` to add a new download source. Register the implementation in DI with the appropriate priority. ```csharp using Wabbajack.Downloaders.Interfaces; using Wabbajack.DTOs; using Wabbajack.DTOs.DownloadStates; using Wabbajack.DTOs.Validation; using Wabbajack.Hashing.xxHash64; using Wabbajack.Paths; using Wabbajack.RateLimiter; // Custom download state public record MyCustomState : IDownloadState { public string Url { get; init; } = ""; public string PrimaryKeyString => Url; public IDownloadState? ConvertToChunkedSeekableState() => null; public bool IsWhitelisted(ServerAllowList allowList) => true; } public class MyCustomDownloader : IDownloader { public Priority Priority => Priority.Normal; public bool CanDownload(Archive archive) => archive.State is MyCustomState; public async Task Download(Archive archive, AbsolutePath destination, IJob job, CancellationToken token) { var state = (MyCustomState)archive.State; using var http = new HttpClient(); await using var stream = await http.GetStreamAsync(state.Url, token); return await destination.WriteAllHashedAsync(stream, token); } public async Task Download(Archive archive, MyCustomState state, AbsolutePath destination, IJob job, CancellationToken token) => await Download(archive, destination, job, token); public async Task Verify(Archive archive, IJob job, CancellationToken token) { var state = (MyCustomState)archive.State; using var http = new HttpClient(); var resp = await http.SendAsync( new HttpRequestMessage(HttpMethod.Head, state.Url), token); return resp.IsSuccessStatusCode; } public Task Prepare() => Task.FromResult(true); public bool IsAllowed(ServerAllowList allowList, IDownloadState state) => true; public IEnumerable MetaIni(Archive a) { var s = (MyCustomState)a.State; yield return $"directURL={s.Url}"; } public IDownloadState? Resolve(IReadOnlyDictionary iniData) => iniData.TryGetValue("directURL", out var url) ? new MyCustomState { Url = url } : null; } ``` ``` -------------------------------- ### Configure Modlist Compilation with CompilerSettings Source: https://context7.com/wabbajack-tools/wabbajack/llms.txt Manually configure all aspects of modlist compilation using CompilerSettings. Specify paths, game details, metadata, profile information, and fine-grained file control. Use this for custom configurations or when automatic inference is not sufficient. ```csharp using Wabbajack.Compiler; using Wabbajack.DTOs; using Wabbajack.Paths; var settings = new CompilerSettings { // Paths Source = (AbsolutePath)@"C:\MO2\SkyrimSE", // MO2 install root Downloads = (AbsolutePath)@"C:\MO2\SkyrimSE\downloads", // MO2 downloads folder OutputFile = (AbsolutePath)@"C:\MyList\MyList.wabbajack",// Output .wabbajack file ModListImage= (AbsolutePath)@"C:\MyList\cover.png", // Game Game = Game.SkyrimSpecialEdition, OtherGames = new[] { Game.SkyrimVR }, // Include stock files from extra games UseGamePaths = true, // Index stock game files as download sources // Modlist metadata ModListName = "My Epic Skyrim List", ModListAuthor = "AuthorName", ModListDescription = "A comprehensive Skyrim SE overhaul.", ModListWebsite = "https://example.com", ModListCommunity = "https://discord.gg/example", Version = Version.Parse("1.0.0.0"), ModlistIsNSFW = false, // Profile Profile = "Default", // MO2 profile to compile AdditionalProfiles = new[] { "PERFORMANCE" }, // Extra profiles to bundle // Fine-grained file control Include = new[] { "CustomConfig".ToRelativePath() }, NoMatchInclude = new[] { "HandmadePatches".ToRelativePath() }, Ignore = new[] { "TestFiles".ToRelativePath() }, // Options UseTextureRecompression = false, // Recompress textures via PHash matching AutoGenerateReport = true, // Generate HTML report after compilation MaxVerificationTime = TimeSpan.FromMinutes(2), }; ```