// We require our constructor to be private so we can // enforce this being an instance (via Instance() above) private RegistryManager(string path, KSP ksp) { this.ksp = ksp; this.path = Path.Combine(path, "registry.json"); lockfilePath = Path.Combine(path, "registry.locked"); // Create a lock for this registry, so we cannot touch it again. if (!GetLock()) { throw new RegistryInUseKraken(lockfilePath); } LoadOrCreate(); // We don't cause an inconsistency error to stop the registry from being loaded, // because then the user can't do anything to correct it. However we're // sure as hell going to complain if we spot one! try { registry.CheckSanity(); } catch (InconsistentKraken kraken) { log.ErrorFormat("Loaded registry with inconsistencies:\r\n\r\n{0}", kraken.InconsistenciesPretty); } }
// Constructor private ModuleInstaller(KSP ksp, IUser user) { User = user; this.ksp = ksp; registry_manager = RegistryManager.Instance(ksp); log.DebugFormat("Creating ModuleInstaller for {0}", ksp.GameDir()); }
public void Valid_MissingVersionData_False() { // Arrange string gamedir = TestData.NewTempDir(); string ckandir = Path.Combine(gamedir, "CKAN"); string buildid = Path.Combine(gamedir, "buildID.txt"); string readme = Path.Combine(gamedir, "readme.txt"); string jsonpath = Path.Combine(ckandir, "compatible_ksp_versions.json"); const string compatible_ksp_versions_json = @"{ ""VersionOfKspWhenWritten"": ""1.4.3"", ""CompatibleKspVersions"": [""1.4""] }"; // Generate a valid game dir except for missing buildID.txt and readme.txt CKAN.Utilities.CopyDirectory(TestData.good_ksp_dir(), gamedir, true); File.Delete(buildid); File.Delete(readme); // Save GameDir/CKAN/compatible_ksp_versions.json Directory.CreateDirectory(ckandir); File.WriteAllText(jsonpath, compatible_ksp_versions_json); // Act CKAN.KSP my_ksp = new CKAN.KSP(gamedir, "missing-ver-test", nullUser); // Assert Assert.IsFalse(my_ksp.Valid); }
public void Constructor_NullMainCompatVer_NoCrash() { // Arrange string gamedir = TestData.NewTempDir(); string ckandir = Path.Combine(gamedir, "CKAN"); string buildid = Path.Combine(gamedir, "buildID.txt"); string readme = Path.Combine(gamedir, "readme.txt"); string jsonpath = Path.Combine(ckandir, "compatible_ksp_versions.json"); const string compatible_ksp_versions_json = @"{ ""VersionOfKspWhenWritten"": null, ""CompatibleKspVersions"": [""1.4""] }"; // Generate a valid game dir except for missing buildID.txt and readme.txt CKAN.Utilities.CopyDirectory(TestData.good_ksp_dir(), gamedir, true); File.Delete(buildid); File.Delete(readme); // Save GameDir/CKAN/compatible_ksp_versions.json Directory.CreateDirectory(ckandir); File.WriteAllText(jsonpath, compatible_ksp_versions_json); // Act & Assert Assert.DoesNotThrow(() => { CKAN.KSP my_ksp = new CKAN.KSP(gamedir, "null-compat-ver-test", nullUser); }); }
public void Setup() { ksp_dir = TestData.NewTempDir(); nullUser = new NullUser(); CKAN.Utilities.CopyDirectory(TestData.good_ksp_dir(), ksp_dir, true); ksp = new CKAN.KSP(ksp_dir, "test", nullUser); }
public void FakeInstance_ValidArgumentsWithDLCs_ManagerHasValidInstance() { string name = "testname"; KspVersion mhVersion = KspVersion.Parse("1.1.0"); KspVersion bgVersion = KspVersion.Parse("1.0.0"); string tempdir = TestData.NewTempDir(); KspVersion version = KspVersion.Parse("1.7.1"); Dictionary <CKAN.DLC.IDlcDetector, KspVersion> dlcs = new Dictionary <CKAN.DLC.IDlcDetector, KspVersion>() { { new CKAN.DLC.MakingHistoryDlcDetector(), mhVersion }, { new CKAN.DLC.BreakingGroundDlcDetector(), bgVersion } }; manager.FakeInstance(name, tempdir, version, dlcs); CKAN.KSP newKSP = new CKAN.KSP(tempdir, name, new NullUser()); CKAN.DLC.MakingHistoryDlcDetector mhDetector = new CKAN.DLC.MakingHistoryDlcDetector(); CKAN.DLC.BreakingGroundDlcDetector bgDetector = new CKAN.DLC.BreakingGroundDlcDetector(); Assert.IsTrue(manager.HasInstance(name)); Assert.IsTrue(mhDetector.IsInstalled(newKSP, out string _, out UnmanagedModuleVersion detectedMhVersion)); Assert.IsTrue(bgDetector.IsInstalled(newKSP, out string _, out UnmanagedModuleVersion detectedBgVersion)); Assert.IsTrue(detectedMhVersion == new UnmanagedModuleVersion(mhVersion.ToString())); Assert.IsTrue(detectedBgVersion == new UnmanagedModuleVersion(bgVersion.ToString())); // Tidy up. CKAN.RegistryManager.Instance(newKSP).ReleaseLock(); System.IO.Directory.Delete(tempdir, true); }
public FakeWin32Registry(CKAN.KSP instance, string autostart = "test") { Instances = new List <Tuple <string, string> > { new Tuple <string, string>("test", instance.GameDir()) }; AutoStartInstance = autostart; }
/// <summary> /// Adds a KSP instance to registry. /// Returns the resulting KSP object. /// </summary> public KSP AddInstance(string name, string path) { var ksp = new KSP(path, User); GetInstances().Add(name, ksp); PopulateRegistryWithInstances(); return(ksp); }
private void DeSerialisationFixes(StreamingContext context) { // Our context is our KSP install. KSP ksp = (KSP)context.Context; registry_version = LATEST_REGISTRY_VERSION; factorioAuthData = ksp?.tryGetFactorioAuthData(); }
/// <summary> /// Renames an instance in the registry and saves. /// </summary> public void RenameInstance(string from, string to) { // TODO: What should we do if our target name already exists? KSP ksp = instances[from]; instances.Remove(from); ksp.Name = to; instances.Add(to, ksp); Configuration.SetRegistryToInstances(instances); }
/// <summary> /// Register the supplied module as having been installed, thereby keeping /// track of its metadata and files. /// </summary> public void RegisterModule(Module mod, IEnumerable <string> absolute_files, KSP ksp) { SealionTransaction(); // But we also want to keep track of all its files. // We start by checking to see if any files are owned by another mod, // if so, we abort with a list of errors. var inconsistencies = new List <string>(); // We always work with relative files, so let's get some! IEnumerable <string> relative_files = absolute_files.Select(x => ksp.ToRelativeGameDir(x)); foreach (string file in relative_files) { // For now, it's always cool if a module wants to register a directory. // We have to flip back to absolute paths to actually test this. if (Directory.Exists(ksp.ToAbsoluteGameDir(file))) { continue; } if (this.installed_files.ContainsKey(file)) { // Woah! Registering an already owned file? Not cool! // (Although if it existed, we should have thrown a kraken well before this.) string owner = this.installed_files[file]; inconsistencies.Add( string.Format("{0} wishes to install {1}, but this file is registered to {2}", mod.identifier, file, owner )); } } if (inconsistencies.Count > 0) { throw new InconsistentKraken(inconsistencies); } // If everything is fine, then we copy our files across. By not doing this // in the loop above, we make sure we don't have a half-registered module // when we throw our exceptinon. // This *will* result in us overwriting who owns a directory, and that's cool, // directories aren't really owned like files are. However because each mod maintains // its own list of files, we'll remove directories when the last mod using them // is uninstalled. foreach (string file in relative_files) { this.installed_files[file] = mod.identifier; } // Finally, register our module proper. var installed = new InstalledModule(ksp, mod, relative_files); installed_modules.Add(mod.identifier, installed); }
/// <summary> /// Set a registry's available modules to the list from just one repo /// </summary> /// <param name="registry_manager">Manager of the regisry of interest</param> /// <param name="ksp">Game instance</param> /// <param name="user">Object for user interaction callbacks</param> /// <param name="repo">Repository to check</param> /// <returns> /// Number of modules found in repo /// </returns> public static bool Update(RegistryManager registry_manager, KSP ksp, IUser user, string repo = null) { if (repo == null) { return(Update(registry_manager, ksp, user, (Uri)null)); } return(Update(registry_manager, ksp, user, new Uri(repo))); }
public static int Update(RegistryManager registry_manager, KSP ksp, IUser user, Boolean clear = true, string repo = null) { if (repo == null) { return Update(registry_manager, ksp, user, clear, (Uri)null); } return Update(registry_manager, ksp, user, clear, new Uri(repo)); }
public static int Update(RegistryManager registry_manager, KSP ksp, IUser user, Boolean clear = true, string repo = null) { if (repo == null) { return(Update(registry_manager, ksp, user, clear, (Uri)null)); } return(Update(registry_manager, ksp, user, clear, new Uri(repo))); }
public FakeWin32Registry(CKAN.KSP instance, string autostart) : this( new List <Tuple <string, string> > { new Tuple <string, string>("test", instance.GameDir()) }, autostart ) { }
/// <summary> /// Create a new fake KSP instance /// </summary> /// <param name="new_name">The name for the new instance.</param> /// <param name="new_path">The loaction of the new instance.</param> /// <param name="version">The version of the new instance. Should have a build number.</param> /// <param name="dlcVersion">The version of the DLC. Null if DLC should be faked.</param> public void FakeInstance(string new_name, string new_path, KspVersion version, string dlcVersion = null) { if (!version.InBuildMap()) { throw new IncorrectKSPVersionKraken(String.Format("The specified KSP version is not a known version: {0}", version.ToString())); } if (Directory.Exists(new_path) && (Directory.GetFiles(new_path).Length != 0 || Directory.GetDirectories(new_path).Length != 0)) { throw new BadInstallLocationKraken("The specified folder already exists and is not empty."); } try { log.DebugFormat("Creating folder structure and text files at {0} for KSP version {1}", Path.GetFullPath(new_path), version.ToString()); // Create a KSP root directory, containing a GameData folder, a buildID.txt/buildID64.txt and a readme.txt Directory.CreateDirectory(new_path); Directory.CreateDirectory(Path.Combine(new_path, "GameData")); Directory.CreateDirectory(Path.Combine(new_path, "Ships")); Directory.CreateDirectory(Path.Combine(new_path, "Ships", "VAB")); Directory.CreateDirectory(Path.Combine(new_path, "Ships", "SPH")); Directory.CreateDirectory(Path.Combine(new_path, "Ships", "@thumbs")); Directory.CreateDirectory(Path.Combine(new_path, "Ships", "@thumbs", "VAB")); Directory.CreateDirectory(Path.Combine(new_path, "Ships", "@thumbs", "SPH")); // Don't write the buildID.txts if we have no build, otherwise it would be -1. if (version.IsBuildDefined) { File.WriteAllText(Path.Combine(new_path, "buildID.txt"), String.Format("build id = {0}", version.Build)); File.WriteAllText(Path.Combine(new_path, "buildID64.txt"), String.Format("build id = {0}", version.Build)); } // Create the readme.txt WITHOUT build number. File.WriteAllText(Path.Combine(new_path, "readme.txt"), String.Format("Version {0}", new KspVersion(version.Major, version.Minor, version.Patch).ToString())); // If a installed DLC should be simulated, we create the needed folder structure and the readme.txt if (!String.IsNullOrEmpty(dlcVersion) && version.CompareTo(new KspVersion(1, 4, 0)) >= 0) { Directory.CreateDirectory(Path.Combine(new_path, "GameData", "SquadExpansion", "MakingHistory")); File.WriteAllText( Path.Combine(new_path, "GameData", "SquadExpansion", "MakingHistory", "readme.txt"), String.Format("Version {0}", dlcVersion)); } // Add the new instance to the registry KSP new_instance = new KSP(new_path, new_name, User); AddInstance(new_instance); } // Thrown by AddInstance() if created instance is not valid. // Thrown f.e. if a write operation didn't complete for unknown reasons. catch (NotKSPDirKraken kraken) { throw kraken; } }
/// <summary> /// Registers the given DLL as having been installed. This provides some support /// for pre-CKAN modules. /// /// Does nothing if the DLL is already part of an installed module. /// </summary> public void RegisterDll(KSP ksp, string absolute_path) { SealionTransaction(); string relative_path = ksp.ToRelativeGameDir(absolute_path); string owner; if (installed_files.TryGetValue(relative_path, out owner)) { log.InfoFormat( "Not registering {0}, it belongs to {1}", relative_path, owner ); return; } // http://xkcd.com/208/ // This regex works great for things like GameData/Foo/Foo-1.2.dll #if (!ONI) Match match = Regex.Match( relative_path, @" ^GameData/ # DLLs only live in GameData (?:.*/)? # Intermediate paths (ending with /) (?<modname>[^.]+) # Our DLL name, up until the first dot. .*\.dll$ # Everything else, ending in dll ", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace ); #else Match match = Regex.Match( relative_path, @"" + @"^" + GameConfig.Constants.ModsDir + @"/ # DLLs only live in GameData (?:.*/)? # Intermediate paths (ending with /) (?<modname>[^.]+) # Our DLL name, up until the first dot. .*\.dll$ # Everything else, ending in dll ", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace ); #endif string modName = match.Groups["modname"].Value; if (modName.Length == 0) { log.WarnFormat("Attempted to index {0} which is not a DLL", relative_path); return; } log.InfoFormat("Registering {0} from {1}", modName, relative_path); // We're fine if we overwrite an existing key. installed_dlls[modName] = relative_path; }
private static string FindDownloadsPath(KSP gameInst) { foreach (string p in downloadPaths) { if (!string.IsNullOrEmpty(p) && Directory.Exists(p)) { return(p); } } return(gameInst.GameDir()); }
/// <summary> /// Sets the current instance. /// Throws an InvalidKSPInstanceKraken if not found. /// </summary> public void SetCurrentInstance(string name) { // TODO: Should we disallow this if _CurrentInstance is already set? if (!GetInstances().ContainsKey(name)) { throw new InvalidKSPInstanceKraken(name); } _CurrentInstance = GetInstances()[name]; }
/// <summary> /// Returns an instance of the registry manager for the KSP install. /// The file `registry.json` is assumed. /// </summary> public static RegistryManager Instance(KSP ksp) { string directory = ksp.CkanDir(); if (!singleton.ContainsKey(directory)) { log.DebugFormat("Preparing to load registry at {0}", directory); singleton[directory] = new RegistryManager(directory, ksp); } return singleton[directory]; }
/// <summary> /// Returns an instance of the registry manager for the KSP install. /// The file `registry.json` is assumed. /// </summary> public static RegistryManager Instance(KSP ksp) { string directory = ksp.CkanDir(); if (!registryCache.ContainsKey(directory)) { log.DebugFormat("Preparing to load registry at {0}", directory); registryCache[directory] = new RegistryManager(directory, ksp); } return registryCache[directory]; }
/// <summary> /// Returns the prefered KSP instance, or null if none can be found. /// /// This works by checking to see if we're in a KSP dir first, then the /// registry for an autostart instance, then will try to auto-populate /// by scanning for the game. /// /// This *will not* touch the registry if we find a portable install. /// /// This *will* run KSP instance autodetection if the registry is empty. /// /// This *will* set the current instance, or throw an exception if it's already set. /// /// Returns null if we have multiple instances, but none of them are preferred. /// </summary> public KSP GetPreferredInstance() { if (_CurrentInstance != null) { // TODO: Throw a better exception throw new KSPManagerKraken("Tried to set KSP instance twice!"); } _CurrentInstance = _GetPreferredInstance(); return(_CurrentInstance); }
public void SetCurrentInstanceByPath(string path) { KSP ksp = new KSP(path, "custom", User); if (ksp.Valid) { CurrentInstance = ksp; } else { throw new NotKSPDirKraken(ksp.GameDir()); } }
/// <summary> /// Adds a KSP instance to registry. /// Returns the resulting KSP object. /// </summary> public KSP AddInstance(string name, KSP ksp_instance) { if (ksp_instance.Valid) { instances.Add(name, ksp_instance); Win32Registry.SetRegistryToInstances(instances, AutoStartInstance); } else { throw new NotKSPDirKraken(ksp_instance.GameDir()); } return(ksp_instance); }
/// <summary> /// Adds a KSP instance to registry. /// Returns the resulting KSP object. /// </summary> public KSP AddInstance(string name, string path) { try { var ksp = new KSP(path, User); GetInstances().Add(name, ksp); PopulateRegistryWithInstances(); return(ksp); } catch (NotKSPDirKraken e) { return(null); } }
public void CloneInstance_BadInstance_ThrowsNotKSPDirKraken() { string badName = "badInstance"; string tempdir = TestData.NewTempDir(); CKAN.KSP badKSP = new CKAN.KSP(TestData.bad_ksp_dirs().First(), "badDir", new NullUser()); Assert.Throws <NotKSPDirKraken>(() => manager.CloneInstance(badKSP, badName, tempdir)); Assert.IsFalse(manager.HasInstance(badName)); // Tidy up System.IO.Directory.Delete(tempdir, true); }
/// <summary> /// Adds a KSP instance to registry. /// Returns the resulting KSP object. /// </summary> public KSP AddInstance(KSP ksp_instance) { if (ksp_instance.Valid) { string name = ksp_instance.Name; instances.Add(name, ksp_instance); Configuration.SetRegistryToInstances(instances); } else { throw new NotKSPDirKraken(ksp_instance.GameDir()); } return ksp_instance; }
/// <summary> /// Download the given mod. Returns the filename it was saved to. /// /// If no filename is provided, the standard_name() will be used. /// /// </summary> /// <param name="filename">Filename.</param> public string Download(CkanModule module, string filename = null) { // Generate a temporary file if none is provided. if (filename == null) { filename = module.StandardName(); } Console.WriteLine(" * Downloading " + filename + "..."); string fullPath = Path.Combine(KSP.DownloadCacheDir(), filename); log.DebugFormat("Downloading {0} to {1}", module.download, fullPath); WebClient agent = new WebClient(); try { agent.DownloadFile(module.download, fullPath); } catch (Exception ex) { // Clean up our file, it's unlikely to be complete. // It's okay if this fails. try { log.DebugFormat("Removing {0} after web error failure", fullPath); File.Delete(fullPath); } catch { // Apparently we need a catch, even if we do nothing. } if (ex is System.Net.WebException && Regex.IsMatch(ex.Message, "authentication or decryption has failed")) { Console.WriteLine("\nOh no! Our download failed!\n"); Console.WriteLine("\t{0}\n", ex.Message); Console.WriteLine("If you're on Linux, try running:\n"); Console.WriteLine("\tmozroots --import --ask-remove\n"); Console.WriteLine("on the command-line to update your certificate store, and try again.\n"); // TODO: Throw an exception that signals we need to exit, rather than // stopping all other code from tidying up. (We do this for now, so // we don't have ugly stack-traces on things we kinda expect.) Environment.Exit(MainClass.EXIT_ERROR); } throw; } return(fullPath); }
/// <summary> /// Download and update the local CKAN meta-info. /// Optionally takes a URL to the zipfile repo to download. /// Returns the number of unique modules updated. /// </summary> public static bool UpdateAllRepositories(RegistryManager registry_manager, KSP ksp, NetModuleCache cache, IUser user) { SortedDictionary <string, Repository> sortedRepositories = registry_manager.registry.Repositories; List <CkanModule> allAvail = new List <CkanModule>(); foreach (KeyValuePair <string, Repository> repository in sortedRepositories) { log.InfoFormat("About to update {0}", repository.Value.name); SortedDictionary <string, int> downloadCounts; List <CkanModule> avail = UpdateRegistry(repository.Value.uri, ksp, user, out downloadCounts); registry_manager.registry.SetDownloadCounts(downloadCounts); if (avail == null) { // Report failure if any repo fails, rather than losing half the list. // UpdateRegistry will have alerted the user to specific errors already. return(false); } else { log.InfoFormat("Updated {0}", repository.Value.name); // Merge all the lists allAvail.AddRange(avail); } } // Save allAvail to the registry if we found anything if (allAvail.Count > 0) { registry_manager.registry.SetAllAvailable(allAvail); // Save our changes. registry_manager.Save(enforce_consistency: false); ShowUserInconsistencies(registry_manager.registry, user); List <CkanModule> metadataChanges = GetChangedInstalledModules(registry_manager.registry); if (metadataChanges.Count > 0) { HandleModuleChanges(metadataChanges, user, ksp, cache); } // Registry.Available is slow, just return success, // caller can check it if it's really needed return(true); } else { // Return failure return(false); } }
/// <summary> /// Gets the ModuleInstaller instance associated with the passed KSP instance. Creates a new ModuleInstaller instance if none exists. /// </summary> /// <returns>The ModuleInstaller instance.</returns> /// <param name="ksp_instance">Current KSP instance.</param> /// <param name="user">IUser implementation.</param> public static ModuleInstaller GetInstance(KSP ksp_instance, IUser user) { ModuleInstaller instance; // Check in the list of instances if we have already created a ModuleInstaller instance for this KSP instance. if (!instances.TryGetValue(ksp_instance.GameDir().ToLower(), out instance)) { // Create a new instance and insert it in the static list. instance = new ModuleInstaller(ksp_instance, user); instances.Add(ksp_instance.GameDir().ToLower(), instance); } return(instance); }
/// <summary> /// Gets the ModuleInstaller instance associated with the passed KSP instance. Creates a new ModuleInstaller instance if none exists. /// </summary> /// <returns>The ModuleInstaller instance.</returns> /// <param name="ksp_instance">Current KSP instance.</param> /// <param name="user">IUser implementation.</param> public static ModuleInstaller GetInstance(KSP ksp_instance, IUser user) { ModuleInstaller instance; // Check in the list of instances if we have already created a ModuleInstaller instance for this KSP instance. if (!instances.TryGetValue(ksp_instance.GameDir().ToLower(), out instance)) { // Create a new instance and insert it in the static list. instance = new ModuleInstaller(ksp_instance, user); instances.Add(ksp_instance.GameDir().ToLower(), instance); } return instance; }
private void UpdateRepo(object sender, DoWorkEventArgs e) { try { KSP current_instance1 = CurrentInstance; Repo.Update(RegistryManager.Instance(CurrentInstance), current_instance1.Version(), (Uri)null); } catch (MissingCertificateKraken ex) { m_ErrorDialog.ShowErrorDialog(ex.ToString()); } catch (Exception) { m_ErrorDialog.ShowErrorDialog("Failed to connect to repository"); } }
public static int Update(RegistryManager registry_manager, KSP ksp, IUser user, Boolean clear = true, Uri repo = null) { // Use our default repo, unless we've been told otherwise. if (repo == null) { repo = default_ckan_repo; } UpdateRegistry(repo, registry_manager.registry, ksp, user, clear); // Save our changes! registry_manager.Save(); // Return how many we got! return(registry_manager.registry.Available(ksp.Version()).Count); }
/// <summary> /// Download and update the local CKAN meta-info. /// Optionally takes a URL to the zipfile repo to download. /// Returns the number of unique modules updated. /// </summary> public static int UpdateAllRepositories(RegistryManager registry_manager, KSP ksp, IUser user) { // If we handle multiple repositories, we will call ClearRegistry() ourselves... registry_manager.registry.ClearAvailable(); // TODO this should already give us a pre-sorted list SortedDictionary <string, Repository> sortedRepositories = registry_manager.registry.Repositories; foreach (KeyValuePair <string, Repository> repository in sortedRepositories) { log.InfoFormat("About to update {0}", repository.Value.name); UpdateRegistry(repository.Value.uri, registry_manager.registry, ksp, user, false); } // Return how many we got! return(registry_manager.registry.Available(ksp.Version()).Count); }
public static int Update(RegistryManager registry_manager, KSP ksp, IUser user, Boolean clear = true, Uri repo = null) { // Use our default repo, unless we've been told otherwise. if (repo == null) { repo = default_ckan_repo; } UpdateRegistry(repo, registry_manager.registry, ksp, user, clear); // Save our changes! registry_manager.Save(); // Return how many we got! return registry_manager.registry.Available(ksp.Version()).Count; }
/// <summary> /// Download and update the local CKAN meta-info. /// Optionally takes a URL to the zipfile repo to download. /// Returns the number of unique modules updated. /// </summary> public static int UpdateAllRepositories(RegistryManager registry_manager, KSP ksp, IUser user) { // If we handle multiple repositories, we will call ClearRegistry() ourselves... registry_manager.registry.ClearAvailable(); // TODO this should already give us a pre-sorted list SortedDictionary<string, Repository> sortedRepositories = registry_manager.registry.Repositories; foreach (KeyValuePair<string, Repository> repository in sortedRepositories) { log.InfoFormat("About to update {0}", repository.Value.name); UpdateRegistry(repository.Value.uri, registry_manager.registry, ksp, user, false); } // Save our changes. registry_manager.Save(); // Return how many we got! return registry_manager.registry.Available(ksp.Version()).Count; }
private void AddNewButton_Click(object sender, EventArgs e) { if (m_BrowseKSPFolder.ShowDialog() == DialogResult.OK) { KSP instance; try { instance = new KSP(m_BrowseKSPFolder.SelectedPath, GUI.user); } catch (NotKSPDirKraken){ GUI.user.displayError("Directory {0} is not valid KSP directory.", new object[] {m_BrowseKSPFolder.SelectedPath}); return; } string instanceName = manager.GetNextValidInstanceName("New instance"); manager.AddInstance(instanceName, instance); UpdateInstancesList(); } }
/// <summary> /// Creates a copy of the provided argument, or a known-good KSP install if passed null. /// Use .KSP to access the KSP object itself. /// </summary> public DisposableKSP(string directory_to_clone = null, string registry_file = null) { directory_to_clone = directory_to_clone ?? good_ksp; disposable_dir = TestData.NewTempDir(); TestData.CopyDirectory(directory_to_clone, disposable_dir); // If we've been given a registry file, then copy it into position before // creating our KSP object. if (registry_file != null) { string registry_dir = Path.Combine(disposable_dir, "CKAN"); string registry_path = Path.Combine(registry_dir, "registry.json"); Directory.CreateDirectory(registry_dir); File.Copy(registry_file, registry_path, true); } KSP = new KSP(disposable_dir, NullUser.User); }
// We require our constructor to be private so we can // enforce this being an instance (via Instance() above) private RegistryManager(string path, KSP ksp) { this.ksp = ksp; this.path = Path.Combine(path, "registry.json"); LoadOrCreate(); // We don't cause an inconsistency error to stop the registry from being loaded, // because then the user can't do anything to correct it. However we're // sure as hell going to complain if we spot one! try { registry.CheckSanity(); } catch (InconsistentKraken kraken) { log.ErrorFormat("Loaded registry with inconsistencies:\n\n{0}", kraken.InconsistenciesPretty); } }
/// <summary> /// Creates a copy of the provided argument, or a known-good KSP install if passed null. /// Use .KSP to access the KSP object itself. /// </summary> public DisposableKSP(string directoryToClone = null, string registryFile = null) { directoryToClone = directoryToClone ?? _goodKsp; _disposableDir = TestData.NewTempDir(); TestData.CopyDirectory(directoryToClone, _disposableDir); // If we've been given a registry file, then copy it into position before // creating our KSP object. if (registryFile != null) { var registryDir = Path.Combine(_disposableDir, "CKAN"); var registryPath = Path.Combine(registryDir, "registry.json"); Directory.CreateDirectory(registryDir); File.Copy(registryFile, registryPath, true); } KSP = new KSP(_disposableDir, NullUser.User); Logging.Initialize(); }
private void AddNewButton_Click(object sender, EventArgs e) { if (_instanceDialog.ShowDialog() != DialogResult.OK) return; if (!File.Exists(_instanceDialog.FileName)) return; KSP instance; var path = Path.GetDirectoryName(_instanceDialog.FileName); try { instance = new KSP(path, GUI.user); } catch (NotKSPDirKraken) { GUI.user.displayError("Directory {0} is not valid KSP directory.", new object[] { path }); return; } var instanceName = Path.GetFileName(path); instanceName = _manager.GetNextValidInstanceName(instanceName); _manager.AddInstance(instanceName, instance); UpdateInstancesList(); }
/// <summary> /// Deregister a module, which must already have its files removed, thereby /// forgetting abouts its metadata and files. /// /// Throws an InconsistentKraken if not all files have been removed. /// </summary> public void DeregisterModule(KSP ksp, string module) { SealionTransaction(); var inconsistencies = new List<string>(); var absolute_files = installed_modules[module].Files.Select(ksp.ToAbsoluteGameDir); // Note, this checks to see if a *file* exists; it doesn't // trigger on directories, which we allow to still be present // (they may be shared by multiple mods. foreach (var absolute_file in absolute_files.Where(File.Exists)) { inconsistencies.Add(string.Format( "{0} is registered to {1} but has not been removed!", absolute_file, module)); } if (inconsistencies.Count > 0) { // Uh oh, what mess have we got ourselves into now, Inconsistency Kraken? throw new InconsistentKraken(inconsistencies); } // Okay, all the files are gone. Let's clear our metadata. foreach (string rel_file in installed_modules[module].Files) { installed_files.Remove(rel_file); } // Bye bye, module, it's been nice having you visit. installed_modules.Remove(module); }
/// <summary> /// Given a stanza and an open zipfile, returns all files that would be installed /// for this stanza. /// /// If a KSP instance is provided, it will be used to generate output paths, otherwise these will be null. /// /// Throws a BadInstallLocationKraken if the install stanza targets an /// unknown install location (eg: not GameData, Ships, etc) /// /// Throws a BadMetadataKraken if the stanza resulted in no files being returned. /// </summary> /// <exception cref="BadInstallLocationKraken">Thrown when the installation path is not valid according to the spec.</exception> internal static List<InstallableFile> FindInstallableFiles(ModuleInstallDescriptor stanza, ZipFile zipfile, KSP ksp) { string installDir; bool makeDirs; var files = new List<InstallableFile> (); // Normalize the path before doing everything else // TODO: This really should happen in the ModuleInstallDescriptor itself. stanza.install_to = KSPPathUtils.NormalizePath(stanza.install_to); // Convert our stanza to a standard `file` type. This is a no-op if it's // already the basic type. stanza = stanza.ConvertFindToFile(zipfile); if (stanza.install_to == "GameData" || stanza.install_to.StartsWith("GameData/")) { // The installation path can be either "GameData" or a sub-directory of "GameData" // but it cannot contain updirs if (stanza.install_to.Contains("/../") || stanza.install_to.EndsWith("/..")) throw new BadInstallLocationKraken("Invalid installation path: " + stanza.install_to); string subDir = stanza.install_to.Substring("GameData".Length); // remove "GameData" subDir = subDir.StartsWith("/") ? subDir.Substring(1) : subDir; // remove a "/" at the beginning, if present // Add the extracted subdirectory to the path of KSP's GameData installDir = ksp == null ? null : (KSPPathUtils.NormalizePath(ksp.GameData() + "/" + subDir)); makeDirs = true; } else if (stanza.install_to.StartsWith("Ships")) { // Don't allow directory creation in ships directory makeDirs = false; switch (stanza.install_to) { case "Ships": installDir = ksp == null ? null : ksp.Ships(); break; case "Ships/VAB": installDir = ksp == null ? null : ksp.ShipsVab(); break; case "Ships/SPH": installDir = ksp == null ? null : ksp.ShipsSph(); break; case "Ships/@thumbs": installDir = ksp == null ? null : ksp.ShipsThumbs(); break; case "Ships/@thumbs/VAB": installDir = ksp == null ? null : ksp.ShipsThumbsVAB(); break; case "Ships/@thumbs/SPH": installDir = ksp == null ? null : ksp.ShipsThumbsSPH(); break; default: throw new BadInstallLocationKraken("Unknown install_to " + stanza.install_to); } } else switch (stanza.install_to) { case "Tutorial": installDir = ksp == null ? null : ksp.Tutorial(); makeDirs = true; break; case "Scenarios": installDir = ksp == null ? null : ksp.Scenarios(); makeDirs = true; break; case "GameRoot": installDir = ksp == null ? null : ksp.GameDir(); makeDirs = false; break; default: throw new BadInstallLocationKraken("Unknown install_to " + stanza.install_to); } // O(N^2) solution, as we're walking the zipfile for each stanza. // Surely there's a better way, although this is fast enough we may not care. foreach (ZipEntry entry in zipfile) { // Skips things not prescribed by our install stanza. if (! stanza.IsWanted(entry.Name)) { continue; } // Prepare our file info. InstallableFile file_info = new InstallableFile { source = entry, makedir = makeDirs, destination = null }; // If we have a place to install it, fill that in... if (installDir != null) { // Get the full name of the file. string outputName = entry.Name; // Update our file info with the install location file_info.destination = TransformOutputName(stanza.file, outputName, installDir); } files.Add(file_info); } // If we have no files, then something is wrong! (KSP-CKAN/CKAN#93) if (files.Count == 0) { // We have null as the first argument here, because we don't know which module we're installing throw new BadMetadataKraken(null, String.Format("No files found in {0} to install!", stanza.file)); } return files; }
/// <summary> /// Registers the given DLL as having been installed. This provides some support /// for pre-CKAN modules. /// /// Does nothing if the DLL is already part of an installed module. /// </summary> public void RegisterDll(KSP ksp, string absolute_path) { SealionTransaction(); string relative_path = ksp.ToRelativeGameDir(absolute_path); string owner; if (installed_files.TryGetValue(relative_path, out owner)) { log.InfoFormat( "Not registering {0}, it belongs to {1}", relative_path, owner ); return; } // http://xkcd.com/208/ // This regex works great for things like GameData/Foo/Foo-1.2.dll Match match = Regex.Match( relative_path, @" ^GameData/ # DLLs only live in GameData (?:.*/)? # Intermediate paths (ending with /) (?<modname>[^.]+) # Our DLL name, up until the first dot. .*\.dll$ # Everything else, ending in dll ", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace ); string modName = match.Groups["modname"].Value; if (modName.Length == 0) { log.WarnFormat("Attempted to index {0} which is not a DLL", relative_path); return; } log.InfoFormat("Registering {0} from {1}", modName, relative_path); // We're fine if we overwrite an existing key. installed_dlls[modName] = relative_path; }
public void Dispose() { Directory.Delete(disposable_dir, true); KSP.Dispose(); KSP = null; // In case .Dispose() was called manually. }
public InstalledModuleFile(string path, KSP ksp) { string absolute_path = ksp.ToAbsoluteGameDir(path); sha1_sum = Sha1Sum(absolute_path); }
/// <summary> /// Given a module and an open zipfile, return all the files that would be installed /// for this module. /// /// If a KSP instance is provided, it will be used to generate output paths, otherwise these will be null. /// /// Throws a BadMetadataKraken if the stanza resulted in no files being returned. /// </summary> public static List<InstallableFile> FindInstallableFiles(CkanModule module, ZipFile zipfile, KSP ksp) { var files = new List<InstallableFile> (); try { // Use the provided stanzas, or use the default install stanza if they're absent. if (module.install != null && module.install.Length != 0) { foreach (ModuleInstallDescriptor stanza in module.install) { files.AddRange(FindInstallableFiles(stanza, zipfile, ksp)); } } else { ModuleInstallDescriptor default_stanza = ModuleInstallDescriptor.DefaultInstallStanza(module.identifier, zipfile); files.AddRange(FindInstallableFiles(default_stanza, zipfile, ksp)); } } catch (BadMetadataKraken kraken) { // Decorate our kraken with the current module, as the lower-level // methods won't know it. kraken.module = module; throw; } return files; }
public void Setup() { ksp_dir = TestData.NewTempDir(); TestData.CopyDirectory(TestData.good_ksp_dir(), ksp_dir); ksp = new CKAN.KSP(ksp_dir,NullUser.User); }
/// <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 = Net.Download(repo); // 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<CkanModule> metadataChanges = new List<CkanModule>(); foreach (var identifierModulePair in old_available) { var identifier = identifierModulePair.Key; if (registry.IsInstalled(identifier)) { var installedVersion = registry.InstalledVersion(identifier); 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 var metadata = registry.available_modules[identifier].module_version[installedVersion]; if (!old_available.ContainsKey(identifier) || !old_available[identifier].module_version.ContainsKey(installedVersion)) { continue; } var oldMetadata = old_available[identifier].module_version[installedVersion]; bool same = true; if ((metadata.install == null) != (oldMetadata.install == null) || (metadata.install != null && metadata.install.Length != oldMetadata.install.Length)) { same = false; } else { if(metadata.install != null) for (int i = 0; i < metadata.install.Length; i++) { if (metadata.install[i].file != oldMetadata.install[i].file) { same = false; break; } if (metadata.install[i].install_to != oldMetadata.install[i].install_to) { same = false; break; } if ((metadata.install[i].filter == null) != (oldMetadata.install[i].filter == null)) { same = false; break; } if(metadata.install[i].filter != null) if (!metadata.install[i].filter.SequenceEqual(oldMetadata.install[i].filter)) { same = false; break; } if ((metadata.install[i].filter_regexp == null) != (oldMetadata.install[i].filter_regexp == null)) { same = false; break; } if(metadata.install[i].filter_regexp != null) if (!metadata.install[i].filter_regexp.SequenceEqual(oldMetadata.install[i].filter_regexp)) { same = false; break; } } } if (!same) { metadataChanges.Add(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].version.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 NetAsyncDownloader(new NullUser())); } } // 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); }
/// <summary> /// Given a module and a path to a zipfile, returns all the files that would be installed /// from that zip for this module. /// /// This *will* throw an exception if the file does not exist. /// /// Throws a BadMetadataKraken if the stanza resulted in no files being returned. /// /// If a KSP instance is provided, it will be used to generate output paths, otherwise these will be null. /// </summary> // TODO: Document which exception! public static List<InstallableFile> FindInstallableFiles(CkanModule module, string zip_filename, KSP ksp) { // `using` makes sure our zipfile gets closed when we exit this block. using (ZipFile zipfile = new ZipFile(zip_filename)) { log.DebugFormat("Searching {0} using {1} as module", zip_filename, module); return FindInstallableFiles(module, zipfile, ksp); } }
public InstalledModule(KSP ksp, Module module, IEnumerable<string> relative_files) { install_time = DateTime.Now; source_module = module; installed_files = new Dictionary<string, InstalledModuleFile>(); foreach (string file in relative_files) { if (Path.IsPathRooted(file)) { throw new PathErrorKraken(file, "InstalledModule *must* have relative paths"); } // IMF needs a KSP object so it can compute the SHA1. installed_files[file] = new InstalledModuleFile(file, ksp); } }
/// <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) { // Use this opportunity to also update the build mappings... kind of hacky ServiceLocator.Container.Resolve<IKspBuildMap>().Refresh(); log.InfoFormat("Downloading {0}", repo); string repo_file = String.Empty; try { repo_file = Net.Download(repo); } catch (System.Net.WebException) { user.RaiseMessage("Connection to {0} could not be established.", repo); return; } // Clear our list of known 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<CkanModule> metadataChanges = new List<CkanModule>(); foreach (var installedModule in registry.InstalledModules) { var identifier = installedModule.identifier; var installedVersion = registry.InstalledVersion(identifier); 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 var metadata = registry.available_modules[identifier].module_version[installedVersion]; var oldMetadata = registry.InstalledModule(identifier).Module; bool same = true; if ((metadata.install == null) != (oldMetadata.install == null) || (metadata.install != null && metadata.install.Length != oldMetadata.install.Length)) { same = false; } else { if(metadata.install != null) for (int i = 0; i < metadata.install.Length; i++) { if (metadata.install[i].file != oldMetadata.install[i].file) { same = false; break; } if (metadata.install[i].install_to != oldMetadata.install[i].install_to) { same = false; break; } if (metadata.install[i].@as != oldMetadata.install[i].@as) { same = false; break; } if ((metadata.install[i].filter == null) != (oldMetadata.install[i].filter == null)) { same = false; break; } if(metadata.install[i].filter != null) if (!metadata.install[i].filter.SequenceEqual(oldMetadata.install[i].filter)) { same = false; break; } if ((metadata.install[i].filter_regexp == null) != (oldMetadata.install[i].filter_regexp == null)) { same = false; break; } if(metadata.install[i].filter_regexp != null) if (!metadata.install[i].filter_regexp.SequenceEqual(oldMetadata.install[i].filter_regexp)) { same = false; break; } } } if (!RelationshipsAreEquivalent(metadata.conflicts, oldMetadata.conflicts)) same = false; if (!RelationshipsAreEquivalent(metadata.depends, oldMetadata.depends)) same = false; if (!RelationshipsAreEquivalent(metadata.recommends, oldMetadata.recommends)) same = false; if (metadata.provides != oldMetadata.provides) { if (metadata.provides == null || oldMetadata.provides == null) same = false; else if (!metadata.provides.OrderBy(i => i).SequenceEqual(oldMetadata.provides.OrderBy(i => i))) same = false; } if (!same) { metadataChanges.Add(registry.available_modules[identifier].module_version[installedVersion]); } } if (metadataChanges.Any()) { var sb = new StringBuilder(); for (var i = 0; i < metadataChanges.Count; i++) { var module = metadataChanges[i]; sb.AppendLine(string.Format("- {0} {1}", module.identifier, module.version)); } if(user.RaiseYesNoDialog(string.Format(@"The following mods have had their metadata changed since last update: {0} You should reinstall them in order to preserve consistency with the repository. Do you wish to reinstall now?", sb))) { ModuleInstaller installer = ModuleInstaller.GetInstance(ksp, new NullUser()); // New upstream metadata may break the consistency of already installed modules // e.g. if user installs modules A and B and then later up A is made to conflict with B // This is perfectly normal and shouldn't produce an error, therefore we skip enforcing // consistency. However, we will show the user any inconsistencies later on. // Use the identifiers so we use the overload that actually resolves relationships // Do each changed module one at a time so a failure of one doesn't cause all the others to fail foreach (var changedIdentifier in metadataChanges.Select(i => i.identifier)) { try { installer.Upgrade( new[] { changedIdentifier }, new NetAsyncModulesDownloader(new NullUser()), enforceConsistency: false ); } // Thrown when a dependency couldn't be satisfied catch(ModuleNotFoundKraken) { log.WarnFormat("Skipping installation of {0} due to relationship error.", changedIdentifier); user.RaiseMessage("Skipping installation of {0} due to relationship error.", changedIdentifier); } // Thrown when a conflicts relationship is violated catch (InconsistentKraken) { log.WarnFormat("Skipping installation of {0} due to relationship error.", changedIdentifier); user.RaiseMessage("Skipping installation of {0} due to relationship error.", changedIdentifier); } } } } // 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); }
/// <summary> /// Register the supplied module as having been installed, thereby keeping /// track of its metadata and files. /// </summary> public void RegisterModule(Module mod, IEnumerable<string> absolute_files, KSP ksp) { SealionTransaction(); // But we also want to keep track of all its files. // We start by checking to see if any files are owned by another mod, // if so, we abort with a list of errors. var inconsistencies = new List<string>(); // We always work with relative files, so let's get some! IEnumerable<string> relative_files = absolute_files.Select(x => ksp.ToRelativeGameDir(x)); // For now, it's always cool if a module wants to register a directory. // We have to flip back to absolute paths to actually test this. foreach (string file in relative_files.Where(file => !Directory.Exists(ksp.ToAbsoluteGameDir(file)))) { string owner; if (installed_files.TryGetValue(file, out owner)) { // Woah! Registering an already owned file? Not cool! // (Although if it existed, we should have thrown a kraken well before this.) inconsistencies.Add( string.Format("{0} wishes to install {1}, but this file is registered to {2}", mod.identifier, file, owner )); } } if (inconsistencies.Count > 0) { throw new InconsistentKraken(inconsistencies); } // If everything is fine, then we copy our files across. By not doing this // in the loop above, we make sure we don't have a half-registered module // when we throw our exceptinon. // This *will* result in us overwriting who owns a directory, and that's cool, // directories aren't really owned like files are. However because each mod maintains // its own list of files, we'll remove directories when the last mod using them // is uninstalled. foreach (string file in relative_files) { installed_files[file] = mod.identifier; } // Finally, register our module proper. var installed = new InstalledModule(ksp, mod, relative_files); installed_modules.Add(mod.identifier, installed); }
public void Dispose() { var registry = RegistryManager.Instance(KSP); if (registry != null) { registry.Dispose(); } var i = 6; while (--i < 0) { try { // Now that the lockfile is closed, we can remove the directory Directory.Delete(_disposableDir, true); } catch (IOException) { // We silently catch this exception because we expect failures } catch (Exception ex) { throw new AssertionException(_failureMessage, ex); } } //proceed to dispose our wrapped KSP object KSP.Dispose(); KSP = null; }
private static int Update(RegistryManager registry_manager, KSP ksp, IUser user, Boolean clear = true, Uri repo = null) { // Use our default repo, unless we've been told otherwise. if (repo == null) { repo = default_ckan_repo; } UpdateRegistry(repo, registry_manager.registry, ksp, user, clear); // Save our changes! registry_manager.Save(enforce_consistency: false); ShowUserInconsistencies(registry_manager.registry, user); // Return how many we got! return registry_manager.registry.Available(ksp.VersionCriteria()).Count; }
public void SetCurrentInstanceByPath(string name) { // TODO: Should we disallow this if _CurrentInstance is already set? CurrentInstance = new KSP(name, User); }
/// <summary> /// Ensures all files for this module have relative paths. /// Called when upgrading registry versions. Should be a no-op /// if called on newer registries.</summary> public void Renormalise(KSP ksp) { var normalised_installed_files = new Dictionary<string, InstalledModuleFile>(); foreach (KeyValuePair<string, InstalledModuleFile> tuple in installed_files) { string path = KSPPathUtils.NormalizePath(tuple.Key); if (Path.IsPathRooted(path)) { path = ksp.ToRelativeGameDir(path); } normalised_installed_files[path] = tuple.Value; } installed_files = normalised_installed_files; }
/// <summary> /// Adds a KSP instance to registry. /// Returns the resulting KSP object. /// </summary> public KSP AddInstance(string name, KSP ksp_instance) { instances.Add(name, ksp_instance); Win32Registry.SetRegistryToInstances(instances, AutoStartInstance); return ksp_instance; }