private void DownloadArchives() { var missing = ModList.Archives.Where(a => !HashedArchives.ContainsKey(a.Hash)).ToList(); Info("Missing {0} archives", missing.Count); Info("Getting Nexus API Key, if a browser appears, please accept"); NexusAPIKey = NexusAPI.GetNexusAPIKey(); DownloadMissingArchives(missing); }
public bool DownloadArchive(Archive archive, bool download) { try { switch (archive) { case NexusMod a: string url; try { url = NexusAPI.GetNexusDownloadLink(a as NexusMod, NexusAPIKey, !download); if (!download) { return(true); } } catch (Exception ex) { Info($"{a.Name} - Error Getting Nexus Download URL - {ex.Message}"); return(false); } Info($"Downloading Nexus Archive - {archive.Name} - {a.GameName} - {a.ModID} - {a.FileID}"); DownloadURLDirect(archive, url); return(true); case MEGAArchive a: return(DownloadMegaArchive(a, download)); case GoogleDriveMod a: return(DownloadGoogleDriveArchive(a, download)); case MODDBArchive a: return(DownloadModDBArchive(archive, (archive as MODDBArchive).URL, download)); case MediaFireArchive a: return(false); //return DownloadMediaFireArchive(archive, a.URL, download); case DirectURLArchive a: return(DownloadURLDirect(archive, a.URL, headers: a.Headers, download: download)); } } catch (Exception ex) { Utils.Log($"Download error for file {archive.Name}"); Utils.Log(ex.ToString()); return(false); } return(false); }
private void DownloadMissingArchives(List <Archive> missing) { missing.PMap(archive => { switch (archive) { case NexusMod a: Info($"Downloading Nexus Archive - {archive.Name} - {a.GameName} - {a.ModID} - {a.FileID}"); string url; try { url = NexusAPI.GetNexusDownloadLink(a as NexusMod, NexusAPIKey); } catch (Exception ex) { Info($"{a.Name} - Error Getting Nexus Download URL - {ex.Message}"); return; } DownloadURLDirect(archive, url); break; case GoogleDriveMod a: DownloadGoogleDriveArchive(a); break; case MODDBArchive a: DownloadModDBArchive(archive, (archive as MODDBArchive).URL); break; case MediaFireArchive a: DownloadMediaFireArchive(archive, a.URL); break; case DirectURLArchive a: DownloadURLDirect(archive, a.URL, headers: a.Headers); break; default: break; } }); }
private void DownloadArchives() { var missing = ModList.Archives.Where(a => !HashedArchives.ContainsKey(a.Hash)).ToList(); Info("Missing {0} archives", missing.Count); Info("Getting Nexus API Key, if a browser appears, please accept"); NexusAPIKey = NexusAPI.GetNexusAPIKey(); var user_status = NexusAPI.GetUserStatus(NexusAPIKey); if (!user_status.is_premium) { Info($"Automated installs with Wabbajack requires a premium nexus account. {user_status.name} is not a premium account"); return; } DownloadMissingArchives(missing); return; }
private void AskToEndorse() { var mods = ModList.Archives .OfType <NexusMod>() .GroupBy(f => (f.GameName, f.ModID)) .Select(mod => mod.First()) .ToArray(); var result = MessageBox.Show( $"Installation has completed, but you have installed {mods.Length} from the Nexus, would you like to" + " endorse these mods to show support to the authors? It will only take a few moments.", "Endorse Mods?", MessageBoxButton.YesNo, MessageBoxImage.Question); if (result != MessageBoxResult.Yes) { return; } // Shuffle mods so that if we hit a API limit we don't always miss the same mods var r = new Random(); for (var i = 0; i < mods.Length; i++) { var a = r.Next(mods.Length); var b = r.Next(mods.Length); var tmp = mods[a]; mods[a] = mods[b]; mods[b] = tmp; } mods.PMap(mod => { var er = NexusAPI.EndorseMod(mod, NexusAPIKey); Utils.Log($"Endorsed {mod.GameName} - {mod.ModID} - Result: {er.message}"); }); Info("Done! You may now exit the application!"); }
public void Build(ModList lst) { Text($"### {lst.Name} - Installation Summary"); Text( $"#### Download Summary ({lst.Archives.Count} archives - {lst.Archives.Sum(a => a.Size).ToFileSizeString()})"); foreach (var archive in SortArchives(lst.Archives)) { var hash = archive.Hash.FromBase64().ToHEX(); switch (archive) { case NexusMod m: var profile = m.UploaderProfile.Replace("/games/", "/" + NexusAPI.ConvertGameName(m.GameName).ToLower() + "/"); NoWrapText( $"* [{m.Name}](http://nexusmods.com/{NexusAPI.ConvertGameName(m.GameName)}/mods/{m.ModID})"); NoWrapText($" * Author : [{m.UploadedBy}]({profile})"); NoWrapText($" * Version : {m.Version}"); break; case MODDBArchive m: NoWrapText($"* MODDB - [{m.Name}]({m.URL})"); break; case MEGAArchive m: NoWrapText($"* MEGA - [{m.Name}]({m.URL})"); break; case GoogleDriveMod m: NoWrapText( $"* GoogleDrive - [{m.Name}](https://drive.google.com/uc?id={m.Id}&export=download)"); break; case DirectURLArchive m: NoWrapText($"* URL - [{m.Name} - {m.URL}]({m.URL})"); break; } NoWrapText($" * Size : {archive.Size.ToFileSizeString()}"); NoWrapText($" * SHA256 : [{hash}](https://www.virustotal.com/gui/file/{hash})"); } Text("\n\n"); var patched = lst.Directives.OfType <PatchedFromArchive>().OrderBy(p => p.To).ToList(); Text($"#### Summary of ({patched.Count}) patches"); foreach (var directive in patched) { NoWrapText( $"* Applying {directive.Patch.Length} byte patch `{directive.FullPath}` to create `{directive.To}`"); } var files = lst.Directives.OrderBy(d => d.To).ToList(); Text($"\n\n### Install Plan of ({files.Count}) files"); Text("(ignoring files that are directly copied from archives or listed in the patches section above)"); foreach (var directive in files.OrderBy(f => f.GetType().Name).ThenByDescending(f => f.To)) { switch (directive) { case FromArchive f: //NoWrapText($"* `{f.To}` from `{f.FullPath}`"); break; case CleanedESM i: NoWrapText($"* `{i.To}` by applying a patch to a game ESM ({i.SourceESMHash})"); break; case RemappedInlineFile i: NoWrapText($"* `{i.To}` by remapping the contents of an inline file"); break; case InlineFile i: NoWrapText($"* `{i.To}` from `{i.SourceData.Length.ToFileSizeString()}` file included in modlist"); break; case CreateBSA i: NoWrapText( $"* `{i.To}` by creating a BSA of files found in `{Consts.BSACreationDir}\\{i.TempID}`"); break; } } var inlined = lst.Directives.OfType <InlineFile>() .Select(f => (f.To, "inlined", f.SourceData.Length)) .Concat(lst.Directives .OfType <PatchedFromArchive>() .Select(f => (f.To, "patched", f.Patch.Length))) .ToHashSet() .OrderByDescending(f => f.Length); NoWrapText("\n\n### Summary of inlined files in this installer"); foreach (var inline in inlined) { NoWrapText($"* {inline.Length.ToFileSizeString()} for {inline.Item2} file {inline.To}"); } }
public void Build(ModList lst) { Text($"### {lst.Name} - Installation Summary"); Text($"#### Download Summary ({lst.Archives.Count} archives)"); foreach (var archive in SortArchives(lst.Archives)) { switch (archive) { case NexusMod m: var profile = m.UploaderProfile.Replace("/games/", "/" + NexusAPI.ConvertGameName(m.GameName).ToLower() + "/"); NoWrapText($"* [{m.UploadedBy}]({profile}) - [{m.Name}](http://nexusmods.com/{NexusAPI.ConvertGameName(m.GameName)}/mods/{m.ModID})"); break; case MODDBArchive m: NoWrapText($"* MODDB - [{m.Name}]({m.URL})"); break; case MEGAArchive m: NoWrapText($"* MEGA - [{m.Name}]({m.URL})"); break; case GoogleDriveMod m: NoWrapText($"* GoogleDrive - [{m.Name}](https://drive.google.com/uc?id={m.Id}&export=download)"); break; case DirectURLArchive m: NoWrapText($"* URL - [{m.Name} - {m.URL}]({m.URL})"); break; } } Text($"\n\n"); var patched = lst.Directives.OfType <PatchedFromArchive>().OrderBy(p => p.To).ToList(); Text($"#### Summary of ({patched.Count}) patches"); foreach (var directive in patched) { NoWrapText($"* Applying {directive.Patch.Length} byte patch `{directive.FullPath}` to create `{directive.To}`"); } var files = lst.Directives.OrderBy(d => d.To).ToList(); Text($"\n\n### Install Plan of ({files.Count}) files"); Text($"(ignoring files that are directly copied from archives or listed in the patches section above)"); foreach (var directive in files) { switch (directive) { case FromArchive f: //NoWrapText($"* `{f.To}` from `{f.FullPath}`"); break; case CleanedESM i: NoWrapText($"* `{i.To}` by applying a patch to a game ESM ({i.SourceESMHash})"); break; case RemappedInlineFile i: NoWrapText($"* `{i.To}` by remapping the contents of a inline file"); break; case InlineFile i: NoWrapText($"* `{i.To}` from `{i.SourceData.Length}` byte file included in modlist"); break; case CreateBSA i: NoWrapText($"* `{i.To}` by creating a BSA of files found in `{Consts.BSACreationDir}\\{i.TempID}`"); break; } } }
private Archive ResolveArchive(string sha, IDictionary <string, IndexedArchive> archives) { if (archives.TryGetValue(sha, out var found)) { if (found.IniData == null) { Error("No download metadata found for {0}, please use MO2 to query info or add a .meta file and try again.", found.Name); } var general = found.IniData.General; if (general == null) { Error("No General section in mod metadata found for {0}, please use MO2 to query info or add the info and try again.", found.Name); } Archive result; if (general.directURL != null && general.directURL.StartsWith("https://drive.google.com")) { var regex = new Regex("((?<=id=)[a-zA-Z0-9_-]*)|(?<=\\/file\\/d\\/)[a-zA-Z0-9_-]*"); var match = regex.Match(general.directURL); result = new GoogleDriveMod() { Id = match.ToString() }; } else if (general.directURL != null && general.directURL.StartsWith(Consts.MegaPrefix)) { result = new MEGAArchive() { URL = general.directURL }; } else if (general.directURL != null && general.directURL.StartsWith("https://www.dropbox.com/")) { var uri = new UriBuilder((string)general.directURL); var query = HttpUtility.ParseQueryString(uri.Query); if (query.GetValues("dl").Count() > 0) { query.Remove("dl"); } query.Set("dl", "1"); uri.Query = query.ToString(); result = new DirectURLArchive() { URL = uri.ToString() }; } else if (general.directURL != null && general.directURL.StartsWith("https://www.moddb.com/downloads/start")) { result = new MODDBArchive() { URL = general.directURL }; } else if (general.directURL != null && general.directURL.StartsWith("http://www.mediafire.com/file/")) { Error("Mediafire links are not currently supported"); return(null); /*result = new MediaFireArchive() * { * URL = general.directURL * };*/ } else if (general.directURL != null) { var tmp = new DirectURLArchive() { URL = general.directURL }; if (general.directURLHeaders != null) { tmp.Headers = new List <string>(); tmp.Headers.AddRange(general.directURLHeaders.Split('|')); } result = tmp; } else if (general.manualURL != null) { result = new ManualURLArchive() { URL = general.manualURL.ToString() }; } else if (general.modID != null && general.fileID != null && general.gameName != null) { var nm = new NexusMod() { GameName = general.gameName, FileID = general.fileID, ModID = general.modID, Version = general.version ?? "0.0.0.0" }; var info = NexusAPI.GetModInfo(nm, NexusKey); nm.Author = info.author; nm.UploadedBy = info.uploaded_by; nm.UploaderProfile = info.uploaded_users_profile_url; result = nm; } else { Error("No way to handle archive {0} but it's required by the modpack", found.Name); return(null); } result.Name = found.Name; result.Hash = found.File.Hash; result.Meta = found.Meta; Info($"Checking link for {found.Name}"); var installer = new Installer(null, "", s => Utils.Log(s)); installer.NexusAPIKey = NexusKey; if (!installer.DownloadArchive(result, false)) { Error($"Unable to resolve link for {found.Name}. If this is hosted on the nexus the file may have been removed."); } return(result); } Error("No match found for Archive sha: {0} this shouldn't happen", sha); return(null); }
public void Compile() { Info($"Indexing {MO2Folder}"); VFS.AddRoot(MO2Folder); Info($"Indexing {GamePath}"); VFS.AddRoot(GamePath); var mo2_files = Directory.EnumerateFiles(MO2Folder, "*", SearchOption.AllDirectories) .Where(p => p.FileExists()) .Select(p => new RawSourceFile(VFS.Lookup(p)) { Path = p.RelativeTo(MO2Folder) }); var game_files = Directory.EnumerateFiles(GamePath, "*", SearchOption.AllDirectories) .Where(p => p.FileExists()) .Select(p => new RawSourceFile(VFS.Lookup(p)) { Path = Path.Combine(Consts.GameFolderFilesDir, p.RelativeTo(GamePath)) }); var loot_path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LOOT"); Info($"Indexing {loot_path}"); VFS.AddRoot(loot_path); var loot_files = Directory.EnumerateFiles(loot_path, "userlist.yaml", SearchOption.AllDirectories) .Where(p => p.FileExists()) .Select(p => new RawSourceFile(VFS.Lookup(p)) { Path = Path.Combine(Consts.LOOTFolderFilesDir, p.RelativeTo(loot_path)) }); Info($"Indexing Archives"); IndexedArchives = Directory.EnumerateFiles(MO2DownloadsFolder) .Where(f => Consts.SupportedArchives.Contains(Path.GetExtension(f))) .Where(f => File.Exists(f + ".meta")) .Select(f => new IndexedArchive() { File = VFS.Lookup(f), Name = Path.GetFileName(f), IniData = (f + ".meta").LoadIniFile(), Meta = File.ReadAllText(f + ".meta") }) .ToList(); Info($"Indexing Files"); IndexedFiles = IndexedArchives.PMap(f => { Status($"Finding files in {Path.GetFileName(f.File.FullPath)}"); return(VFS.FilesInArchive(f.File)); }) .SelectMany(fs => fs) .OrderByDescending(f => f.TopLevelArchive.LastModified) .GroupBy(f => f.Hash) .ToDictionary(f => f.Key, f => f.AsEnumerable()); Info("Searching for mod files"); AllFiles = mo2_files.Concat(game_files) .Concat(loot_files) .ToList(); Info("Found {0} files to build into mod list", AllFiles.Count); ExtraFiles = new ConcurrentBag <Directive>(); ModInis = Directory.EnumerateDirectories(Path.Combine(MO2Folder, "mods")) .Select(f => { var mod_name = Path.GetFileName(f); var meta_path = Path.Combine(f, "meta.ini"); if (File.Exists(meta_path)) { return(mod_name, meta_path.LoadIniFile()); } return(null, null); }) .Where(f => f.Item2 != null) .ToDictionary(f => f.Item1, f => f.Item2); var stack = MakeStack(); Info("Running Compilation Stack"); var results = AllFiles.PMap(f => RunStack(stack, f)).ToList(); // Add the extra files that were generated by the stack Info($"Adding {ExtraFiles.Count} that were generated by the stack"); results = results.Concat(ExtraFiles).ToList(); var nomatch = results.OfType <NoMatch>(); Info("No match for {0} files", nomatch.Count()); foreach (var file in nomatch) { Info(" {0}", file.To); } if (nomatch.Count() > 0) { if (IgnoreMissingFiles) { Info("Continuing even though files were missing at the request of the user."); } else { Info("Exiting due to no way to compile these files"); return; } } InstallDirectives = results.Where(i => !(i is IgnoredDirectly)).ToList(); Info("Getting nexus api_key please click authorize if a browser window appears"); NexusKey = NexusAPI.GetNexusAPIKey(); User = NexusAPI.GetUserStatus(NexusKey); if (!User.is_premium) { Info($"User {User.name} is not a premium Nexus user, cannot continue"); } GatherArchives(); BuildPatches(); ModList = new ModList() { Archives = SelectedArchives, Directives = InstallDirectives, Name = MO2Profile }; GenerateReport(); PatchExecutable(); ResetMembers(); ShowReport(); Info("Done Building Modpack"); }