/// <summary> /// Gets the content of the site of the passed URL and parses it for ModInfos. /// </summary> /// <param name="url">The URL of the site to parse the ModInfos from.</param> /// <returns>The ModInfos parsed from the site of the passed URL.</returns> public ModInfo GetModInfo(string url) { var modInfo = new ModInfo { SiteHandlerName = Name, ModURL = ReduceToPlainUrl(url) }; ParseSite(ref modInfo); return modInfo; }
/// <summary> /// Gets the content of the site of the passed URL and parses it for ModInfos. /// </summary> /// <param name="url">The URL to the KSP forum site to parse the ModInfos from.</param> /// <returns>The ModInfos parsed from the site of the passed URL.</returns> public static ModInfo GetModInfo(string url) { ModInfo modInfo = new ModInfo(); modInfo.SiteHandlerName = Name; modInfo.ModURL = url; if (ParseSite(Www.Load(url), ref modInfo)) return modInfo; else return null; }
/// <summary> /// Gets the content of the site of the passed URL and parses it for ModInfos. /// </summary> /// <param name="url">The URL of the site to parse the ModInfos from.</param> /// <returns>The ModInfos parsed from the site of the passed URL.</returns> public ModInfo GetModInfo(string url) { var parts = GetUrlParts(url); var modInfo = new ModInfo { SiteHandlerName = Name, ModURL = ReduceToPlainUrl(url), Name = parts[3], Author = parts[2] }; ParseSite(ref modInfo); return modInfo; }
/// <summary> /// Gets the content of the site of the passed URL and parses it for ModInfos. /// </summary> /// <param name="url">The URL of the site to parse the ModInfos from.</param> /// <returns>The ModInfos parsed from the site of the passed URL.</returns> public ModInfo GetModInfo(string url) { ModInfo modInfo = new ModInfo { SiteHandlerName = Name, ModURL = ReduceToPlainUrl(url) }; if (ParseSite(url, ref modInfo)) return modInfo; return null; }
/// <summary> /// Checks if updates are available for the passed mod. /// </summary> /// <param name="modInfo">The ModInfos of the mod to check for updates.</param> /// <param name="newModInfo">A reference to an empty ModInfo to write the updated ModInfos to.</param> /// <returns>True if there is an update, otherwise false.</returns> public bool CheckForUpdates(ModInfo modInfo, ref ModInfo newModInfo) { newModInfo = GetModInfo(modInfo.ModURL); if (string.IsNullOrEmpty(modInfo.ChangeDate) && !string.IsNullOrEmpty(newModInfo.ChangeDate)) return true; else if (!string.IsNullOrEmpty(modInfo.ChangeDate) && !string.IsNullOrEmpty(newModInfo.ChangeDate)) return modInfo.ChangeDateAsDateTime < newModInfo.ChangeDateAsDateTime; return false; }
/// <summary> /// Adds a MOD to the TreeView. /// </summary> /// <param name="fileNames">Paths to the Zip-Files of the KSP mods.</param> /// <param name="showCollisionDialog">Flag to show/hide the collision dialog.</param> internal static void AddModsAsync(string[] fileNames, bool showCollisionDialog = true) { if (fileNames.Length > 0) { ModInfo[] modInfos = new ModInfo[fileNames.Length]; for (int i = 0; i < fileNames.Length; ++i) modInfos[i] = new ModInfo { LocalPath = fileNames[i], Name = Path.GetFileNameWithoutExtension(fileNames[i]) }; AddModsAsync(modInfos, showCollisionDialog); } else { Messenger.AddError(Messages.MSG_ADD_MODS_FAILED_PARAM_EMPTY_FILENAMES); } }
/// <summary> /// Creates a list of DownloadInfos from a GitHub release /// </summary> /// <param name="modInfo">The mod to generate the list from</param> /// <returns>A list of one or more DownloadInfos for the most recent release of the selected repository</returns> private static List<DownloadInfo> GetDownloadInfo(ModInfo modInfo) { var htmlDoc = new HtmlWeb().Load(GetPathToReleases(modInfo.ModURL)); htmlDoc.OptionFixNestedTags = true; var releases = new List<DownloadInfo>(); var nodesrel = htmlDoc.DocumentNode.SelectNodes("//*[@class='release label-latest']/div[2]/ul/li/a"); var nodespre = htmlDoc.DocumentNode.SelectNodes("//*[@class='release label-prerelease'][1]/div[2]/ul/li/a"); if (nodesrel != null) { foreach (var s in nodesrel) { var url = "https://github.com" + s.Attributes["href"].Value; if (!url.Contains("releases")) continue; var dInfo = new DownloadInfo { DownloadURL = url, Filename = GetUrlParts(url).Last(), Name = Path.GetFileNameWithoutExtension(GetUrlParts(url).Last()) }; releases.Add(dInfo); } } if (nodespre != null) { foreach (var s in nodespre) { var url = "https://github.com" + s.Attributes["href"].Value; if (!url.Contains("releases")) continue; var versionNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@class='release label-prerelease']/div[1]/ul/li[1]/a/span[2]").InnerText; var dInfo = new DownloadInfo { DownloadURL = url, Filename = GetUrlParts(url).Last(), Name = "Pre-release: " + versionNode + ": " + Path.GetFileNameWithoutExtension(GetUrlParts(url).Last()) }; releases.Add(dInfo); } } return releases; }
/// <summary> /// Downloads the mod. /// </summary> /// <param name="modInfo">The infos of the mod. Must have at least ModURL and LocalPath</param> /// <param name="downloadProgressCallback">Callback function for download progress.</param> /// <returns>True if the mod was downloaded.</returns> public bool DownloadMod(ref ModInfo modInfo, DownloadProgressCallback downloadProgressCallback = null) { if (modInfo == null) return false; var downloadInfos = GetDownloadInfo(modInfo); DownloadInfo selected = null; // If any of the nodes came back as a prerelease, notify the user that there are pre-release nodes foreach (var d in downloadInfos) { if (!d.Name.Contains("Pre-release")) continue; var dlg = MessageBox.Show("This download contains a pre-release version. This version might not be stable.", Messages.MSG_TITLE_ATTENTION, MessageBoxButtons.OK); break; } if (downloadInfos.Count > 1) { // create new selection form if more than one download option found var dlg = new frmSelectDownload(downloadInfos); if (dlg.ShowDialog() == DialogResult.OK) { selected = dlg.SelectedLink; } } else if (downloadInfos.Count == 1) { selected = downloadInfos.First(); } else { string msg = string.Format(Messages.MSG_NO_BINARY_DOWNLOAD_FOUND_AT_0, modInfo.SiteHandlerName); MessageBox.Show(msg, Messages.MSG_TITLE_ERROR); Messenger.AddDebug(msg); return false; } if (selected != null) { string downloadUrl = selected.DownloadURL; modInfo.LocalPath = Path.Combine(OptionsController.DownloadPath, selected.Filename); Www.DownloadFile(downloadUrl, modInfo.LocalPath, downloadProgressCallback); } return File.Exists(modInfo.LocalPath); }
/// <summary> /// Downloads the mod. /// </summary> /// <param name="modInfo">The infos of the mod. Must have at least ModURL and LocalPath</param> /// <param name="downloadProgressCallback">Callback function for download progress.</param> /// <returns>True if the mod was downloaded.</returns> public bool DownloadMod(ref ModInfo modInfo, DownloadProgressCallback downloadProgressCallback = null) { if (modInfo == null) return false; HtmlWeb web = new HtmlWeb(); HtmlDocument htmlDoc = web.Load(modInfo.ModURL); htmlDoc.OptionFixNestedTags = true; // get filename from hover text HtmlNode fileNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='content']/section[2]/div[4]/div[2]/ul/li[1]/div[2]/p/a"); HtmlNode fileNode2 = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='content']/section[2]/div[4]/div[2]/ul/li/div[2]/p/a/span"); string downloadURL = GetDownloadURL(modInfo.ModURL); if (fileNode == null || (fileNode.InnerHtml.Contains("...") && fileNode2 == null)) { modInfo.LocalPath = Www.DownloadFile2(downloadURL, OptionsController.DownloadPath, downloadProgressCallback); return !string.IsNullOrEmpty(modInfo.LocalPath) && File.Exists(modInfo.LocalPath); } string filename = string.Empty; if (fileNode.InnerHtml.Contains("...")) filename = fileNode2.Attributes["title"].Value; // Long filename was truncated else filename = fileNode.InnerHtml; modInfo.LocalPath = Path.Combine(OptionsController.DownloadPath, filename); Www.DownloadFile(downloadURL, modInfo.LocalPath, downloadProgressCallback); return File.Exists(modInfo.LocalPath); }
/// <summary> /// Creates a new instance of the ModNode class. /// </summary> public ModNode(ModInfo modInfo) { ModInfo = modInfo; }
/// <summary> /// Checks if updates are available for the passed mod. /// </summary> /// <param name="modInfo">The ModInfos of the mod to check for updates.</param> /// <param name="newModInfo">A reference to an empty ModInfo to write the updated ModInfos to.</param> /// <returns>True if there is an update, otherwise false.</returns> public bool CheckForUpdates(ModInfo modInfo, ref ModInfo newModInfo) { newModInfo = GetModInfo(modInfo.ModURL); return (VersionComparer.CompareVersions(modInfo.Version, newModInfo.Version) == VersionComparer.Result.AisSmallerB); }
/// <summary> /// Downloads the mod. /// </summary> /// <param name="modInfo">The infos of the mod. Must have at least ModURL and LocalPath</param> /// <param name="downloadProgressCallback">Callback function for download progress.</param> /// <returns>True if the mod was downloaded.</returns> public bool DownloadMod(ref ModInfo modInfo, DownloadProgressCallback downloadProgressCallback = null) { if (modInfo == null) return false; string downloadUrl = GetDownloadURL(modInfo); ////string siteContent = www.Load(GetFilesURL(modInfo.ModURL)); ////string filename = GetFileName(siteContent); modInfo.LocalPath = Path.Combine(OptionsController.DownloadPath, GetDownloadName(modInfo)); Www.DownloadFile(downloadUrl, modInfo.LocalPath, downloadProgressCallback); return File.Exists(modInfo.LocalPath); }
/// <summary> /// Loads a mod's source page and extracts mod info from it /// </summary> /// <param name="modInfo">A mod to add info to</param> private static void ParseSite(ref ModInfo modInfo) { var htmlDoc = new HtmlWeb().Load(GetPathToDownloads(modInfo.ModURL)); htmlDoc.OptionFixNestedTags = true; // To scrape the fields, now using HtmlAgilityPack and XPATH search strings. // Easy way to get XPATH search: use chrome, inspect element, highlight the needed data and right-click and copy XPATH HtmlNode authorNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='repo-owner-link']"); HtmlNode nameNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='content']/div[1]/div[1]/div[1]/header/div/div[2]/h1/a"); HtmlNode updateNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='uploaded-files']/tbody/tr[2]/td[5]/div/time"); HtmlNode downloadNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='uploaded-files']/tbody/tr[2]/td[4]"); modInfo.Author = authorNode.InnerHtml; modInfo.Name = nameNode.InnerHtml; modInfo.ChangeDateAsDateTime = DateTime.Parse(updateNode.Attributes["datetime"].Value); modInfo.Downloads = downloadNode.InnerHtml; }
/// <summary> /// Downloads the mod. /// </summary> /// <param name="modInfo">The infos of the mod. Must have at least ModURL and LocalPath</param> /// <param name="downloadProgressCallback">Callback function for download progress.</param> /// <returns>True if the mod was downloaded.</returns> public bool DownloadMod(ref ModInfo modInfo, DownloadProgressCallback downloadProgressCallback = null) { if (modInfo == null) return false; string downloadUrl = GetDownloadPath(GetPathToDownloads(modInfo.ModURL)); modInfo.LocalPath = Path.Combine(OptionsController.DownloadPath, downloadUrl.Split("/").Last()); Www.DownloadFile(downloadUrl, modInfo.LocalPath, downloadProgressCallback); return File.Exists(modInfo.LocalPath); }
/// <summary> /// Downloads the mod. /// </summary> /// <param name="modInfo">The infos of the mod. Must have at least ModURL and LocalPath</param> /// <param name="downloadProgressCallback">Callback function for download progress.</param> /// <returns>True if the mod was downloaded.</returns> public bool DownloadMod(ref ModInfo modInfo, DownloadProgressCallback downloadProgressCallback = null) { ISiteHandler curseForge = SiteHandlerManager.GetSiteHandlerByName("CurseForge"); return curseForge.DownloadMod(ref modInfo, downloadProgressCallback); }
/// <summary> /// Creates nodes from the ModInfos and adds the nodes to the ModSelection. /// </summary> /// <param name="modInfos">The nodes to add.</param> /// <returns>List of added mods.</returns> internal static List<ModNode> AddMods(ModInfo[] modInfos, bool showCollisionDialog, AsyncTask<List<ModNode>> asyncJob = null) { int doneCount = 0; List<ModNode> addedMods = new List<ModNode>(); foreach (ModInfo modInfo in modInfos) { Messenger.AddInfo(Constants.SEPARATOR); Messenger.AddInfo(string.Format(Messages.MSG_START_ADDING_0, modInfo.Name)); Messenger.AddInfo(Constants.SEPARATOR); try { // already added? ModNode newNode = null; ModNode mod = (string.IsNullOrEmpty(modInfo.ProductID)) ? null : Model[modInfo.ProductID, modInfo.SiteHandlerName]; if (mod == null && !Model.ContainsLocalPath(modInfo.LocalPath)) { try { if (modInfo.LocalPath.EndsWith(Constants.EXT_CRAFT, StringComparison.CurrentCultureIgnoreCase) && File.Exists(modInfo.LocalPath)) modInfo.LocalPath = ModZipCreator.CreateZipOfCraftFile(modInfo.LocalPath); newNode = ModNodeHandler.CreateModNode(modInfo); if (newNode != null) { Model.AddMod(newNode); Messenger.AddInfo(string.Format(Messages.MSG_MOD_ADDED_0, newNode.Text)); } } catch (Exception ex) { Messenger.AddError(string.Format(Messages.MSG_MOD_ERROR_WHILE_READ_ZIP_0_ERROR_MSG_1, string.Empty, ex.Message), ex); } } else if (mod != null && (mod.IsOutdated || modInfo.CreationDateAsDateTime > mod.CreationDateAsDateTime) && OptionsController.ModUpdateBehavior != ModUpdateBehavior.Manualy) { newNode = UpdateMod(modInfo, mod); } else { View.InvokeIfRequired(() => { StringBuilder sb = new StringBuilder(); sb.AppendLine(string.Format(Messages.MSG_MOD_ALREADY_ADDED, modInfo.Name)); sb.AppendLine(); sb.AppendLine(Messages.MSG_SHOULD_MOD_REPLACED); if (MessageBox.Show(View, sb.ToString(), Messages.MSG_TITLE_ATTENTION, MessageBoxButtons.YesNo) == DialogResult.Yes) { ModNode outdatedMod = Model[modInfo.LocalPath]; Messenger.AddInfo(string.Format(Messages.MSG_REPLACING_MOD_0, outdatedMod.Text)); newNode = UpdateMod(modInfo, outdatedMod); Messenger.AddInfo(string.Format(Messages.MSG_MOD_0_REPLACED, newNode.Text)); } }); } if (newNode != null) addedMods.Add(newNode); newNode = null; if (asyncJob != null) asyncJob.PercentFinished = doneCount += 1; } catch (Exception ex) { MessageBox.Show(View, ex.Message, Messages.MSG_TITLE_ERROR); Messenger.AddError(string.Format(Messages.MSG_ADD_MOD_FAILED_0, modInfo.Name), ex); } InvalidateView(); Messenger.AddInfo(Constants.SEPARATOR); } return addedMods; }
/// <summary> /// Creates nodes from the ModInfos and adds the nodes to the ModSelection. /// </summary> /// <param name="modInfos">The nodes to add.</param> internal static void AddModsAsync(ModInfo[] modInfos, bool showCollisionDialog = true) { if (modInfos.Length <= 0) { Messenger.AddError(Messages.MSG_ADD_MODS_FAILED_PARAM_EMPTY_MODINFOS); return; } EventDistributor.InvokeAsyncTaskStarted(Instance); View.SetEnabledOfAllControls(false); View.SetProgressBarStates(true, modInfos.Length, 0); AsyncTask<List<ModNode>> asnyJob = new AsyncTask<List<ModNode>>(); asnyJob.SetCallbackFunctions(() => { return AddMods(modInfos, showCollisionDialog, asnyJob); }, (result, ex) => { EventDistributor.InvokeAsyncTaskDone(Instance); View.SetEnabledOfAllControls(true); View.SetProgressBarStates(false); }, (percentage) => { View.SetProgressBarStates(true, modInfos.Length, percentage); }); asnyJob.Run(); }
/// <summary> /// Gets the ModInfo from a ImportInfo class. /// </summary> /// <param name="importInfo">The ImportInfo class to get the ModInfos from.</param> /// <returns>The ModInfo from a ImportInfo class.</returns> private static ModInfo GetModInfo(ImportInfo importInfo) { ModInfo modInfo = new ModInfo(); if (importInfo.ModInfo != null) modInfo = importInfo.ModInfo; else { modInfo.ModURL = importInfo.ModURL; modInfo.AdditionalURL = importInfo.AdditionalURL; modInfo.LocalPath = importInfo.LocalPath; modInfo.Name = importInfo.Name; modInfo.ProductID = importInfo.ProductID; modInfo.SiteHandlerName = importInfo.SiteHandlerName; } return modInfo; }
private string GetDownloadURL(ModInfo modInfo) { return modInfo.ModURL.EndsWith("/") ? modInfo.ModURL + "download/" + modInfo.Version : modInfo.ModURL + "/download/" + modInfo.Version; }
private string GetDownloadName(ModInfo modInfo) { return (modInfo.Name.Replace(' ', '_') + '-' + modInfo.Version + ".zip"); }
/// <summary> /// Takes a curse project site, fetches the page, and extracts mod info from the page /// </summary> /// <param name="url">URL to a kerbal.curseforge.com project</param> /// <param name="modInfo">Stores the extracted mod data</param> /// <returns>Returns true if successfully extracts data</returns> private bool ParseSite(string url, ref ModInfo modInfo) { // changed to use the curse page as it provides the same info but also game version // there's no good way to get a mod version from curse. Could use file name? Is using update date (best method?) string cursePage = "http://www.curse.com/ksp-mods/kerbal/" + new Uri(url).Segments[2]; HtmlWeb web = new HtmlWeb(); HtmlDocument htmlDoc = web.Load(cursePage); htmlDoc.OptionFixNestedTags = true; // To scrape the fields, now using HtmlAgilityPack and XPATH search strings. // Easy way to get XPATH search: use chrome, inspect element, highlight the needed data and right-click and copy XPATH HtmlNode nameNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='project-overview']/div/div[1]/h2/span/span/span"); HtmlNode idNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='project-overview']/div/div[2]/div/div/div[1]/div[2]/ul[2]/li[8]/a"); HtmlNode createNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='project-overview']/div/div[2]/div/div/div[1]/div[2]/ul[2]/li[6]/abbr"); HtmlNode updateNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='project-overview']/div/div[2]/div/div/div[1]/div[2]/ul[2]/li[5]/abbr"); HtmlNode downloadNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='project-overview']/div/div[2]/div/div/div[1]/div[2]/ul[2]/li[4]"); HtmlNode authorNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='project-overview']/div/div[2]/div/div/div[1]/div[2]/ul[1]/li[1]/a"); HtmlNode gameVersionNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='project-overview']/div/div[2]/div/div/div[1]/div[2]/ul[2]/li[3]"); var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); // Curse stores the date as both text and as Epoch. Go for the most precise value (Epoch). if (nameNode == null) return false; modInfo.Name = nameNode.InnerHtml; modInfo.ProductID = idNode.Attributes["href"].Value.Substring(idNode.Attributes["href"].Value.LastIndexOf("/") + 1); modInfo.CreationDateAsDateTime = epoch.AddSeconds(Convert.ToDouble(createNode.Attributes["data-epoch"].Value)); modInfo.ChangeDateAsDateTime = epoch.AddSeconds(Convert.ToDouble(updateNode.Attributes["data-epoch"].Value)); modInfo.Downloads = downloadNode.InnerHtml.Split(" ")[0]; modInfo.Author = authorNode.InnerHtml; modInfo.KSPVersion = gameVersionNode.InnerHtml.Split(" ")[1]; return true; // more infos could be parsed here (like: short description, Tab content (overview, installation, ...), comments, ...) }
/// <summary> /// Gets the content of the site of the passed URL and parses it for ModInfos. /// </summary> /// <param name="url">The URL of the site to parse the ModInfos from.</param> /// <returns>The ModInfos parsed from the site of the passed URL.</returns> public ModInfo GetModInfo(string url) { string modInfoUrl = MODINFO_URL + (url.Split(new string[] { "/" }, StringSplitOptions.None).ToList())[4]; if (string.IsNullOrEmpty(modInfoUrl)) return null; string content = Www.Load(modInfoUrl); if (string.IsNullOrEmpty(content)) return null; JObject jObject = JObject.Parse(content); var modInfo = new ModInfo { SiteHandlerName = Name, ModURL = url, ProductID = GetString(jObject["id"]), Name = GetString(jObject["name"]), Downloads = GetString(jObject["downloads"]), Author = GetString(jObject["author"]), Version = GetVersion(jObject["versions"] as JToken), KSPVersion = GetKSPVersion(jObject["versions"] as JToken) }; ////modInfo.CreationDate = kerbalMod.Versions.Last().Date; // TODO when KS API supports dates from versions return modInfo; }
/// <summary> /// Checks if updates are available for the passed mod. /// </summary> /// <param name="modInfo">The ModInfos of the mod to check for updates.</param> /// <param name="newModInfo">A reference to a empty ModInfo to write the updated ModInfos to.</param> /// <returns>True if there is an update, otherwise false.</returns> public bool CheckForUpdates(ModInfo modInfo, ref ModInfo newModInfo) { newModInfo = GetModInfo(modInfo.ModURL); return modInfo.ChangeDateAsDateTime < newModInfo.ChangeDateAsDateTime; }
/// <summary> /// Creates a tree of TreeNodeMod nodes that represent the content of a mod archive. /// </summary> /// <param name="modInfo">The ModInfo of the mod the create a tree for.</param> /// <param name="silent">Determines if info messages should be added.</param> /// <returns>A tree of TreeNodeMod nodes that represent the content of a mod archive.</returns> public static ModNode CreateModNode(ModInfo modInfo, bool silent = false) { if (File.Exists(modInfo.LocalPath)) { // Get AVC version file informations. if (OptionsController.AVCSupportOnOff) { AVCInfo avcInfo = TryReadAVCVersionFile(modInfo.LocalPath); if (avcInfo != null) ImportAvcInfo(avcInfo, ref modInfo); } // Still no name? Use filename then if (string.IsNullOrEmpty(modInfo.Name)) modInfo.Name = Path.GetFileNameWithoutExtension(modInfo.LocalPath); ModNode node = new ModNode(modInfo); using (IArchive archive = ArchiveFactory.Open(modInfo.LocalPath)) { char seperator = '/'; string extension = Path.GetExtension(modInfo.LocalPath); if (extension != null && extension.Equals(Constants.EXT_RAR, StringComparison.CurrentCultureIgnoreCase)) seperator = '\\'; // create a TreeNode for every archive entry foreach (IArchiveEntry entry in archive.Entries) CreateModNode(entry.FilePath, node, seperator, entry.IsDirectory, silent); } // Destination detection switch (OptionsController.DestinationDetectionType) { case DestinationDetectionType.SmartDetection: // Find installation root node (first folder that contains (Parts or Plugins or ...) if (!FindAndSetDestinationPaths(node) && !silent) Messenger.AddInfo(string.Format(Messages.MSG_ROOT_NOT_FOUND_0, node.Text)); if (OptionsController.CopyToGameData) { if (!silent) Messenger.AddInfo(string.Format(Messages.MSG_DESTINATION_0_SET_TO_GAMEDATA, node.Text)); SetDestinationPaths(node, KSPPathHelper.GetPath(KSPPaths.GameData)); } break; case DestinationDetectionType.SimpleDump: if (!silent) Messenger.AddInfo(string.Format(Messages.MSG_DESTINATION_0_SET_TO_GAMEDATA, node.Text)); SetDestinationPaths(node, KSPPathHelper.GetPath(KSPPaths.GameData)); break; default: throw new ArgumentOutOfRangeException(); } SetToolTips(node); CheckNodesWithDestination(node); return node; } else { if (!silent) Messenger.AddInfo(string.Format(Messages.MSG_MOD_ZIP_NOT_FOUND_0, modInfo.LocalPath)); } return null; }
/// <summary> /// Checks if updates are available for the passed mod. /// </summary> /// <param name="modInfo">The ModInfos of the mod to check for updates.</param> /// <param name="newModInfo">A reference to an empty ModInfo to write the updated ModInfos to.</param> /// <returns>True if there is an update, otherwise false.</returns> public bool CheckForUpdates(ModInfo modInfo, ref ModInfo newModInfo) { newModInfo = GetModInfo(modInfo.ModURL); if (string.IsNullOrEmpty(modInfo.Version) && !string.IsNullOrEmpty(newModInfo.Version)) return true; else if (!string.IsNullOrEmpty(modInfo.Version) && !string.IsNullOrEmpty(newModInfo.Version)) return (VersionComparer.CompareVersions(modInfo.Version, newModInfo.Version) == VersionComparer.Result.AisSmallerB); else if (string.IsNullOrEmpty(modInfo.CreationDate) && !string.IsNullOrEmpty(newModInfo.CreationDate)) return true; else if (!string.IsNullOrEmpty(modInfo.CreationDate) && !string.IsNullOrEmpty(newModInfo.CreationDate)) return modInfo.CreationDateAsDateTime < newModInfo.CreationDateAsDateTime; return false; }
private void ParseSite(ref ModInfo modInfo) { var htmlDoc = new HtmlWeb().Load(modInfo.ModURL); htmlDoc.OptionFixNestedTags = true; // To scrape the fields, now using HtmlAgilityPack and XPATH search strings. // Easy way to get XPATH search: use chrome, inspect element, highlight the needed data and right-click and copy XPATH modInfo.ProductID = GetProductID(modInfo.ModURL); modInfo.CreationDateAsDateTime = GetCreationDate(htmlDoc); modInfo.ChangeDateAsDateTime = GetChangeDate(htmlDoc); if (modInfo.ChangeDateAsDateTime == DateTime.MinValue) modInfo.ChangeDateAsDateTime = modInfo.CreationDateAsDateTime; modInfo.Author = GetAuthor(htmlDoc); modInfo.Name = GetModName(htmlDoc); }
/// <summary> /// Takes a site url and parses the site for mod info /// </summary> /// <param name="modInfo">The modInfo to add data to</param> public void ParseSite(ref ModInfo modInfo) { var htmlDoc = new HtmlWeb().Load(GetPathToReleases(modInfo.ModURL)); htmlDoc.OptionFixNestedTags = true; // To scrape the fields, now using HtmlAgilityPack and XPATH search strings. // Easy way to get XPATH search: use chrome, inspect element, highlight the needed data and right-click and copy XPATH HtmlNode latestRelease = htmlDoc.DocumentNode.SelectSingleNode("//*[@class='release label-latest']"); HtmlNode versionNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@class='release label-latest']/div[1]/ul/li[1]/a/span[2]"); if (versionNode == null) versionNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='js-repo-pjax-container']/div[2]/ul/li[1]/div/div/h3/a/span"); HtmlNode updateNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@class='release label-latest']/div[2]/div/p/time"); if (updateNode == null) updateNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='js-repo-pjax-container']/div[2]/ul/li[1]/span/time"); if (versionNode == null || updateNode == null) Messenger.AddError("Error! Can't parse GitHib version or creation date!"); if (versionNode != null) modInfo.Version = Regex.Replace(versionNode.InnerText, @"[A-z]", string.Empty); if (updateNode != null) modInfo.ChangeDateAsDateTime = DateTime.Parse(updateNode.Attributes["datetime"].Value); }
/// <summary> /// Updates the outdated mod. /// Tries to copy the checked state and destination of a mod and its parts, then uninstalls the outdated mod and installs the new one. /// </summary> /// <param name="newModInfo">The ModeInfo of the new mod.</param> /// <param name="outdatedMod">The root ModNode of the outdated mod.</param> /// <returns>The updated mod.</returns> public static ModNode UpdateMod(ModInfo newModInfo, ModNode outdatedMod) { ModNode newMod = null; try { Messenger.AddInfo(string.Format(Messages.MSG_UPDATING_MOD_0, outdatedMod.Text)); newMod = ModNodeHandler.CreateModNode(newModInfo); newMod.AdditionalURL = outdatedMod.AdditionalURL; newMod.AvcURL = outdatedMod.AvcURL; newMod.Note = outdatedMod.Note; if (OptionsController.ModUpdateBehavior == ModUpdateBehavior.RemoveAndAdd || (!outdatedMod.IsInstalled && !outdatedMod.HasInstalledChilds)) { RemoveOutdatedAndAddNewMod(outdatedMod, newMod); View.InvokeIfRequired(() => { newMod._Checked = false; }); } else { // Find matching file nodes and copy destination from old to new mod. if (ModNodeHandler.TryCopyDestToMatchingNodes(outdatedMod, newMod)) { newMod.ModURL = outdatedMod.ModURL; RemoveOutdatedAndAddNewMod(outdatedMod, newMod); ProcessMods(new ModNode[] { newMod }, true); } else { // No match found -> user must handle update. View.InvokeIfRequired(() => MessageBox.Show(View.ParentForm, string.Format(Messages.MSG_ERROR_UPDATING_MOD_0_FAILED, outdatedMod.Text))); } } Messenger.AddInfo(string.Format(Messages.MSG_MOD_0_UPDATED, newMod.Text)); } catch (Exception ex) { Messenger.AddError(string.Format(Messages.MSG_ERROR_WHILE_UPDATING_MOD_0_ERROR_1, outdatedMod.Text, ex.Message), ex); } return newMod; }
/// <summary> /// Downloads the mod. /// </summary> /// <param name="modInfo">The infos of the mod. Must have at least ModURL and LocalPath</param> /// <param name="downloadProgressCallback">Callback function for download progress.</param> /// <returns>True if the mod was downloaded.</returns> public bool DownloadMod(ref ModInfo modInfo, DownloadProgressCallback downloadProgressCallback = null) { Messenger.AddError("No download support for KSP Forum mods, update check only!"); MessageBox.Show("No download support for KSP Forum mods, update check only!", Messages.MSG_TITLE_ATTENTION); return false; ////if (modInfo == null) //// return false; ////string downloadUrl = GetDownloadUrl(modInfo); ////modInfo.LocalPath = Path.Combine(OptionsController.DownloadPath, GetDownloadName(downloadUrl)); ////Www.DownloadFile(downloadUrl, modInfo.LocalPath, downloadProgressHandler); ////return File.Exists(modInfo.LocalPath); }
/// <summary> /// Adds a mod from HD with given ModInfos. /// </summary> /// <param name="modInfo">The ModInfos of the mod to add.</param> /// <param name="installAfterAdd">Flag that determines if the mod should be installed after adding to the ModSelection.</param> /// <returns>The new added mod (maybe null).</returns> public static ModNode HandleModAddViaModInfo(ModInfo modInfo, bool installAfterAdd) { ModNode newMod = null; List<ModNode> addedMods = AddMods(new ModInfo[] { modInfo }, true, null); if (addedMods.Count > 0 && !string.IsNullOrEmpty(modInfo.Name)) addedMods[0].Text = modInfo.Name; if (installAfterAdd) ProcessMods(addedMods.ToArray()); if (addedMods.Count > 0) newMod = addedMods[0]; return newMod; }