private static async Task <bool> DownloadVersionAsync(ModVersion modVersion) { Console.WriteLine($"Downloading update: {modVersion.Version}..."); List <string> filesToDeleteInScript = new List <string>(); var currentVersion = LocalData.Version; if (currentVersion != null) { var filesToDelete = currentVersion.Checksums.Where(checksum => !modVersion.Checksums.Select(c => c.FilePath) .Contains(checksum.FilePath)) .Select(checksum => checksum.FilePath) .Where(File.Exists).ToList(); if (filesToDelete.Count > 0) { Console.WriteLine($"Deleting {filesToDelete.Count} old file(s)..."); } var allDirectories = new List <string>(); foreach (string filePath in filesToDelete) { if (Utility.FileInUse(filePath)) { filesToDeleteInScript.Add(filePath); continue; } try { File.Delete(filePath); string directory = Path.GetDirectoryName(filePath); if (directory != string.Empty && !allDirectories.Contains(directory)) { List <string> directories = GetDirectories(directory).ToList(); allDirectories.AddRange(directories.Where(d => !allDirectories.Contains(d))); } } catch (IOException ex) { Console.Error.WriteLine($"Failed to delete file: {filePath} ({ex.Message}"); } } foreach (string directory in allDirectories) { try { if (Directory.Exists(directory) && Directory.GetFiles(directory, "*", SearchOption.AllDirectories).Length == 0) { Console.WriteLine($"Deleting empty directory: {directory}"); Directory.Delete(directory, true); } } catch (IOException ex) { Console.Error.WriteLine($"Failed to delete directory: {directory} ({ex.Message})"); } } } List <string> filesToDownload = modVersion.Checksums.Where(checksum => !checksum.MatchesExistingFile()).Select(checksum => checksum.FilePath).ToList(); bool failed = false; if (filesToDownload.Count > 0) { List <string> filesToReplace = new List <string>(); using (var progressBar = new ProgressBar(filesToDownload.Count, $"Downloading files", progressBarOptions)) { var runningTasks = new Task[filesToDownload.Count]; for (int i = 0; i < filesToDownload.Count; ++i) { string filePath = filesToDownload[i]; runningTasks[i] = DownloadFile(filePath, modVersion.Type, modVersion.Version, progressBar, filesToReplace, (success) => { if (success) { progressBar.Tick(); } else { failed = true; } }); } await Task.WhenAll(runningTasks); } if (filesToReplace.Count > 0 || filesToDeleteInScript.Count > 0) { var scriptGenerator = new ScriptGenerator(); scriptGenerator.WriteLine("Waiting for updater to exit...").SleepSeconds(1); filesToReplace.ForEach(filePath => scriptGenerator.MoveFile(filePath + ".new", filePath)); filesToDeleteInScript.ForEach(filePath => scriptGenerator.DeleteFile(filePath)); string scriptFileName = scriptGenerator.GetFileName("update-finish"); File.WriteAllText("version.json.new", JsonConvert.SerializeObject(modVersion, Formatting.None, SerializerSettings)); scriptGenerator.MoveFile("version.json.new", "version.json"); scriptGenerator.DeleteFile(scriptFileName); scriptGenerator.LaunchFile(Assembly.GetEntryAssembly().Location, string.Join(" ", Environment.GetCommandLineArgs().Skip(1))); var script = scriptGenerator.Generate(); File.WriteAllText(scriptFileName, script); Console.WriteLine("Exiting updater to replace files in use..."); await Task.Delay(1000); Process.Start(scriptFileName); Environment.Exit(3); } } if (failed) { return(false); } File.WriteAllText("version.json", JsonConvert.SerializeObject(modVersion, Formatting.None, SerializerSettings)); return(true); }
public static CfanJson createCfanJsonFromModListJson(string file, string name, string title, ModVersion version, string author, string description = "") { ModListJson modList = JsonConvert.DeserializeObject <ModListJson>(File.ReadAllText(file, Encoding.UTF8)); CfanJson cfanJson = createEmptyMetaCfanJson(name, title, version, author, description); cfanJson.modInfo.dependencies = modList.mods.Where(p => p.enabled == ModListJson.ModListJsonTruthy.YES) .Select(p => new ModDependency(p.name)) .ToArray(); cfanJson.suggests = modList.mods.Where(p => p.enabled == ModListJson.ModListJsonTruthy.NO).Select(p => new ModDependency(p.name)).ToArray(); return(cfanJson); }
private static ReleaseFileNameTestDataPoint CreateTestDataPointFromProperties(String ModName, ModVersion ModVersion) { String creationString = $"{ModName}_{ModVersion}.zip"; return(new ReleaseFileNameTestDataPoint { CreationString = creationString, ModName = ModName, ModVersion = ModVersion, ObjectFromFor = ReleaseFileName.For(creationString) }); }
public bool ChangeModStatus(ModMetadata meta, ModVersion version, ModStatus status) { throw new NotImplementedException(); }
private async Task <object> UploadVersionAsync(dynamic args, CancellationToken cancellationToken) { this.RequiresAuthentication(); if (!ParseModType(args, out ModType modType, out Response errorResponse)) { return(await errorResponse); } var file = Request.Files.FirstOrDefault(); UploadVersionDTO data = JsonConvert.DeserializeObject <UploadVersionDTO>(Request.Form.data); if (file == null) { return(await Response.JsonError("No file found", HttpStatusCode.BadRequest)); } if (file.ContentType != "application/zip" && file.ContentType != "application/x-zip-compressed" && file.ContentType != "application/zip-compressed") { return(await Response.JsonError("File is not a zip file", HttpStatusCode.BadRequest)); } // Verify that the version not older or equal to the current latest version if (Data.GetLatestVersion(modType, true, out ModVersion latestVersion)) { if (modType == ModType.Server) { if (!Version.TryParse(data.Version, out Version serverVersion)) { return(await Response.JsonError("The specified version is not valid.", HttpStatusCode.BadRequest)); } var currentServerVersion = Version.Parse(latestVersion.Version); /*if (serverVersion <= currentServerVersion) * { * return await Response.JsonError("The specified version is older or equal to the current version.", HttpStatusCode.BadRequest); * }*/ } else if (modType == ModType.Client) { if (!data.Version.Contains("_")) { return(await Response.JsonError("The specified version is not valid.", HttpStatusCode.BadRequest)); } string[] currentVersions = latestVersion.Version.Split('_'); Version currentClientVersion = Version.Parse(currentVersions[0]); Version currentGameVersion = Version.Parse(currentVersions[1]); string[] versions = data.Version.Split('_'); if (!Version.TryParse(versions[0], out Version clientVersion) || !Version.TryParse(versions[1], out Version gameVersion)) { return(await Response.JsonError("The specified version is not valid.", HttpStatusCode.BadRequest)); } /*if (clientVersion <= currentClientVersion && gameVersion <= currentGameVersion) * return await Response.JsonError("The specified version is older or equal to the current version.", HttpStatusCode.BadRequest);*/ } } var modVersion = new ModVersion { Version = data.Version, ReleaseDate = data.ReleaseDate, DirectoryPath = $"versions/{modType}/{data.Version}", Type = modType }; byte[] archiveBytes = new byte[file.Value.Length]; await file.Value.ReadAsync(archiveBytes, 0, archiveBytes.Length, cancellationToken); var zipStream = new MemoryStream(); await zipStream.WriteAsync(archiveBytes, 0, archiveBytes.Length, cancellationToken); zipStream.Position = 0; try { using (var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Update, true)) { using (var md5 = MD5.Create()) { foreach (ZipArchiveEntry entry in zipArchive.Entries) { // Ignore directories if (entry.Name == string.Empty) { continue; } using (var entryStream = entry.Open()) { var md5Bytes = md5.ComputeHash(entryStream); string md5String = BitConverter.ToString(md5Bytes).Replace("-", "").ToLowerInvariant(); modVersion.Checksums.Add(new ModVersion.FileChecksum { FilePath = entry.FullName, Md5 = md5String }); } } } // Add version.json to archive. var versionEntry = zipArchive.CreateEntry("version.json", CompressionLevel.Optimal); using (var stream = versionEntry.Open()) { string versionJson = await Task.Factory.StartNew(() => JsonConvert.SerializeObject(modVersion), cancellationToken); byte[] bytes = Encoding.UTF8.GetBytes(versionJson); await stream.WriteAsync(bytes, 0, bytes.Length, cancellationToken); } } } catch (InvalidDataException) { return(await Response.JsonError("File is not a valid zip file.", HttpStatusCode.BadRequest)); } Directory.CreateDirectory(modVersion.DirectoryPath); // Save archive using (var fileStream = File.Create(Path.Combine(modVersion.DirectoryPath, "archive.zip"))) { zipStream.Position = 0; byte[] bytes = new byte[zipStream.Length]; await zipStream.ReadAsync(bytes, 0, bytes.Length, cancellationToken); await fileStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken); } zipStream.Dispose(); // Save json file string json = await Task.Factory.StartNew(() => JsonConvert.SerializeObject(modVersion), cancellationToken); File.WriteAllText(Path.Combine(modVersion.DirectoryPath, "version.json"), json, Encoding.UTF8); // Add version to version index if it's not already there. var indexTuple = new Tuple <ModType, string>(modType, modVersion.Version); if (!Data.VersionIndex.Contains(indexTuple)) { Data.VersionIndex.Add(indexTuple); } Data.Versions.Add(modVersion); Data.InvalidateLatestVersion(); await Data.SaveAsync(default);
public ModVersion calculateMaxVersion() { if (modName == "base" && maxVersion == null) { // we assume that mods without no game version specified are only valid for versions below 0.13 return(minVersion == null?ModVersion.maxWithTheSameMinor(new ModVersion("0.12")) : ModVersion.maxWithTheSameMinor(minVersion)); } return(maxVersion); }
public bool IsEqualOrAbove(ModVersion version) { return(Major >= version.Major && Minor >= version.Minor && Patch >= version.Patch); }
public Release(DateTime ReleasedAt, String Sha1String, ReleaseDownloadUrl ReleaseDownloadUrl, ReleaseFileName ReleaseFileName, ModVersion ModVersion, FactorioVersion FactorioVersion, List <Dependency> Dependencies) { DateTimeValidator.ValidateRequiredDateTimeBeforePresent(ReleasedAt, nameof(ReleasedAt)); ObjectValidator.ValidateRequiredObject(Sha1String, nameof(Sha1String)); ObjectValidator.ValidateRequiredObject(ReleaseDownloadUrl, nameof(ReleaseDownloadUrl)); ObjectValidator.ValidateRequiredObject(ReleaseFileName, nameof(ReleaseFileName)); ObjectValidator.ValidateRequiredObject(ModVersion, nameof(ModVersion)); ObjectValidator.ValidateRequiredObject(FactorioVersion, nameof(FactorioVersion)); ListValidator.ValidateRequiredListNotEmpty(Dependencies, nameof(Dependencies)); Regex sha1CaptureRegex = new Regex(Release.Sha1CapturePattern); Match match = sha1CaptureRegex.Match(Sha1String); if (!match.Success) { throw new ArgumentException($"Unable to parse \"{Sha1String}\" to a valid SHA-1 hash due to formatting.", "Sha1String"); } String sha1 = match.Groups[1].Value; if (sha1.Length != Sha1Length) { throw new ArgumentException($"Unable to parse \"{Sha1String}\" to a valid SHA-1 hash due to length. The SHA-1 hash must have a length of exactly {Release.Sha1Length} characters.", "Sha1String"); } if (ModVersion != ReleaseFileName.ModVersion) { throw new ArgumentException("The specified release file name version does not match the specified release version.", "ReleaseFileName"); } this.ReleasedAt = ReleasedAt; this.ReleaseDownloadUrl = new ReleaseDownloadUrl(ReleaseDownloadUrl); this.ReleaseFileName = new ReleaseFileName(ReleaseFileName); this.Sha1 = sha1; this.ModVersion = new ModVersion(ModVersion); this.FactorioVersion = new FactorioVersion(FactorioVersion); // TODO: We may want to write an .AddDependency() method that validates that the dependency we're adding isn't for the release/mod itself. That would generate a circular dependency. this.Dependencies = Dependencies.ConvertAll(dependency => new Dependency(dependency)); }
private static ReleaseTestDataPoint CreateTestDataPointFromProperties(DateTime ReleasedAt, String Sha1String, ReleaseDownloadUrl ReleaseDownloadUrl, ReleaseFileName ReleaseFileName, ModVersion ModVersion, FactorioVersion FactorioVersion, List <Dependency> Dependencies) { return(new ReleaseTestDataPoint { ReleasedAt = ReleasedAt, Sha1 = Sha1String, ReleaseDownloadUrl = ReleaseDownloadUrl, ReleaseFileName = ReleaseFileName, ModVersion = ModVersion, FactorioVersion = FactorioVersion, Dependencies = Dependencies, ObjectFromConstructor = new Release(ReleasedAt, Sha1String, ReleaseDownloadUrl, ReleaseFileName, ModVersion, FactorioVersion, Dependencies) }); }
/// <summary> /// Updates the supplied registry from the URL given. /// This does not *save* the registry. For that, you probably want Repo.Update /// </summary> internal static void UpdateRegistry(Uri repo, Registry registry, KSP ksp, IUser user, Boolean clear = true) { log.InfoFormat("Downloading {0}", repo); string repo_file = String.Empty; try { repo_file = Net.Download(repo); } catch (System.Net.WebException e) { user.RaiseError($"Couldn't download {repo}.", e); return; } // Clear our list of known modules. var old_available = registry.available_modules; if (clear) { registry.ClearAvailable(); } // Check the filetype. FileType type = FileIdentifier.IdentifyFile(repo_file); switch (type) { case FileType.TarGz: UpdateRegistryFromTarGz(repo_file, registry); break; case FileType.Zip: UpdateRegistryFromZip(repo_file, registry); break; default: break; } List <CfanModule> metadataChanges = new List <CfanModule>(); foreach (var identifierModulePair in old_available) { var identifier = identifierModulePair.Key; if (registry.IsInstalled(identifier)) { AbstractVersion abstractVersion = registry.InstalledVersion(identifier); var installedVersion = new ModVersion(abstractVersion.ToString()); if (!(registry.available_modules.ContainsKey(identifier))) { log.InfoFormat("UpdateRegistry, module {0}, version {1} not in repository ({2})", identifier, installedVersion, repo); continue; } if (!registry.available_modules[identifier].module_version.ContainsKey(installedVersion)) { continue; } // if the mod is installed and the metadata is different we have to reinstall it CfanModule metadata = new CfanModule(registry.available_modules[identifier].module_version[installedVersion]); if (!old_available.ContainsKey(identifier) || !old_available[identifier].module_version.ContainsKey(installedVersion)) { continue; } CfanModule oldMetadata = new CfanModule(old_available[identifier].module_version[installedVersion]); bool same = metadata.kind == oldMetadata.kind; if (!same) { metadataChanges.Add(new CfanModule(registry.available_modules[identifier].module_version[installedVersion])); } } } if (metadataChanges.Any()) { string mods = ""; for (int i = 0; i < metadataChanges.Count; i++) { mods += metadataChanges[i].identifier + " " + metadataChanges[i].modVersion.ToString() + ((i < metadataChanges.Count - 1) ? ", " : ""); } if (user.RaiseYesNoDialog(String.Format( @"The following mods have had their metadata changed since last update - {0}. It is advisable that you reinstall them in order to preserve consistency with the repository. Do you wish to reinstall now?", mods))) { ModuleInstaller installer = ModuleInstaller.GetInstance(ksp, new NullUser()); installer.Upgrade(metadataChanges, new NetAsyncModulesDownloader(new NullUser(), ksp.tryGetFactorioAuthData())); } } // Remove our downloaded meta-data now we've processed it. // Seems weird to do this as part of a transaction, but Net.Download uses them, so let's be consistent. file_transaction.Delete(repo_file); }
public GUIMod(CfanModule mod, IRegistryQuerier registry, FactorioVersion current_ksp_version) { IsCKAN = mod is CfanModule; //Currently anything which could alter these causes a full reload of the modlist // If this is ever changed these could be moved into the properties Mod = mod; IsInstalled = registry.IsInstalled(mod.identifier, false); IsInstallChecked = IsInstalled; HasUpdate = registry.HasUpdate(mod.identifier, current_ksp_version); IsIncompatible = !mod.IsCompatibleKSP(current_ksp_version); IsAutodetected = registry.IsAutodetected(mod.identifier); Authors = mod.authors == null ? "N/A" : String.Join(",", mod.authors); var installed_version = registry.InstalledVersion(mod.identifier); AbstractVersion latest_version = null; var ksp_version = mod.getMinFactorioVersion(); CfanModule latest_available = null; try { latest_available = registry.LatestAvailable(mod.identifier, current_ksp_version); if (latest_available != null) { latest_version = latest_available.modVersion; } } catch (ModuleNotFoundKraken) { latest_version = installed_version; } InstalledVersion = installed_version != null?installed_version.ToString() : "-"; // Let's try to find the compatibility for this mod. If it's not in the registry at // all (because it's a DarkKAN mod) then this might fail. CfanModule latest_available_for_any_ksp = null; try { latest_available_for_any_ksp = registry.LatestAvailable(mod.identifier, null); } catch { // If we can't find the mod in the CKAN, but we've a CkanModule installed, then // use that. if (IsCKAN) { latest_available_for_any_ksp = (CfanModule)mod; } } var showInfoFrom = latest_available ?? latest_available_for_any_ksp; // If there's known information for this mod in any form, calculate the highest compatible // KSP. if (showInfoFrom != null) { string minVersion = showInfoFrom.getMinFactorioVersion()?.ToString(); string maxVersion = showInfoFrom.HighestCompatibleKSP()?.ToString(); if (maxVersion != null && ModVersion.isMaxWithTheSameMinor(new ModVersion(maxVersion))) { maxVersion = maxVersion.Replace(int.MaxValue.ToString(), "x"); } if (minVersion != null && maxVersion != null) { KSPCompatibility = minVersion.ToString() + " - " + maxVersion.ToString(); } else if (minVersion != null) { KSPCompatibility = " >= " + minVersion.ToString(); } else if (maxVersion != null) { KSPCompatibility = " <= " + maxVersion.ToString(); } else { KSPCompatibility = "any"; } KSPCompatibilityLong = KSPCompatibility; // If the mod we have installed is *not* the mod we have installed, or we don't know // what we have installed, indicate that an upgrade would be needed. if (installed_version == null || !showInfoFrom.modVersion.Equals(installed_version)) { KSPCompatibilityLong = string.Format("{0} (using mod version {1})", KSPCompatibility, showInfoFrom.modVersion); } } else { // No idea what this mod is, sorry! KSPCompatibility = KSPCompatibilityLong = "unknown"; } if (latest_version != null) { LatestVersion = latest_version.ToString(); } else if (latest_available_for_any_ksp != null) { LatestVersion = latest_available_for_any_ksp.modVersion.ToString(); } else { LatestVersion = "-"; } KSPversion = ksp_version != null?ksp_version.ToString() : "-"; Abstract = mod.@abstract; // If we have homepage provided use that, otherwise use the spacedock page or the github repo so that users have somewhere to get more info than just the abstract. Homepage = "N/A"; if (!string.IsNullOrEmpty(mod.homepage)) { Homepage = mod.homepage; } Identifier = mod.identifier; if (mod.download_size == 0) { DownloadSize = "N/A"; } else if (mod.download_size / 1024.0 < 1) { DownloadSize = "1<KB"; } else { DownloadSize = mod.download_size / 1024 + ""; } Abbrevation = new string(mod.title.Split(' '). Where(s => s.Length > 0).Select(s => s[0]).ToArray()); if (Main.Instance != null) { IsCached = Main.Instance.CurrentInstance.Cache.IsMaybeCachedZip(mod.download); } }
public void ReleaseConstructor_WhenValidParameters_ReturnsCorrectReleasedAt(DateTime releasedAt, String sha1String, ReleaseDownloadUrl releaseDownloadUrl, ReleaseFileName releaseFileName, ModVersion modVersion, FactorioVersion factorioVersion, List <Dependency> dependencies) { var testRelease = new Release(releasedAt, sha1String, releaseDownloadUrl, releaseFileName, modVersion, factorioVersion, dependencies); Assert.Equal(releasedAt, testRelease.ReleasedAt); }
public void CopyConstructor_WhenValidParameters_ReturnsCorrectModVersion(Release release, ModVersion expectedModVersion) { var testRelease = new Release(release); Assert.Equal(expectedModVersion, testRelease.ModVersion); }