/// <summary>Saves the strings for the selected entities into a new file.</summary> /// <param name="parts">The parts to export the strings from.</param> /// <param name="assemblies">The mod assemblies to export teh strinsg from.</param> void GuiActionExportStrings(IEnumerable <PartsRecord> parts, IEnumerable <AssemblyRecord> assemblies) { var partsLocs = parts .SelectMany(x => x.parts) .Select(Extractor.EmitItemsForPart) .SelectMany(x => x) .ToList(); var modulesLocs = assemblies .SelectMany(x => x.assembly.GetTypes()) .Select(Extractor.EmitItemsForType) .SelectMany(x => x) .ToList(); Debug.LogWarningFormat("Export {0} parts strings and {1} modules strings", partsLocs.Count, modulesLocs.Count); var locItems = partsLocs.Union(modulesLocs); var fileName = "strings.cfg"; if (assemblies.Count() == 1) { fileName = assemblies.First().assembly.GetName().Name + "_" + fileName; } var filePath = KspPaths.GetModsDataFilePath(this, "Lang/" + fileName); Directory.CreateDirectory(Path.GetDirectoryName(filePath)); ConfigStore.WriteLocItems(locItems, Localizer.CurrentLanguage, filePath); Debug.LogWarningFormat("Strings are written into: {0}", filePath); ShowCompletionDialog(StringsExportedDlgTitle, FileSavedTxt.Format(filePath)); }
/// <summary>Reads values of the annotated persistent fields from a config file.</summary> /// <param name="filePath"> /// A relative or an absolute path to the file. It's resolved via /// <see cref="KspPaths.MakeAbsPathForGameData"/>. /// </param> /// <param name="type">A type to load fields for.</param> /// <param name="instance"> /// An instance of type <paramref name="type"/>. If it's <c>null</c> then /// only static fields will be loaded. /// </param> /// <param name="nodePath"> /// An optional path in the file. All type's field will be read relative to this part. /// </param> /// <param name="group">A group tag (see <see cref="BasePersistentFieldAttribute"/>).</param> /// <seealso cref="PersistentFieldAttribute"/> public static void ReadFieldsFromFile(string filePath, Type type, object instance, string nodePath = null, string group = StdPersistentGroups.Default) { DebugEx.Fine("Loading persistent fields: file={0}, group=\"{1}\"", KspPaths.MakeRelativePathToGameData(filePath), group ?? "<ALL>"); var node = ConfigNode.Load(KspPaths.MakeAbsPathForGameData(filePath)); if (node != null && nodePath.Length > 0) { node = node.GetNode(nodePath); } if (node != null) { ReadFieldsFromNode(node, type, instance, group: group); } }
/// <summary>Reads values of the annotated persistent fields from a config file.</summary> /// <param name="filePath">A relative or an absolute path to the file. It's resolved via /// <see cref="KspPaths.makePluginPath"/>.</param> /// <param name="type">A type to load fields for.</param> /// <param name="instance">An instance of type <paramref name="type"/>. If it's <c>null</c> then /// only static fields will be loaded.</param> /// <param name = "nodePath">An optional path in the file. All type's field will be read relative /// to this part.</param> /// <param name="group">A group tag (see <see cref="AbstractPersistentFieldAttribute"/>).</param> /// <seealso cref="PersistentFieldAttribute"/> public static void ReadFieldsFromFile(string filePath, Type type, object instance, string nodePath = null, string group = StdPersistentGroups.Default) { Logger.logInfo("Loading persistent fields: file={0}, group=\"{1}\"", filePath, group ?? "<ALL>"); var node = ConfigNode.Load(KspPaths.makePluginPath(filePath)); if (node != null && nodePath.Length > 0) { node = node.GetNode(nodePath); } if (node != null) { ReadFieldsFromNode(node, type, instance, group: group); } }
void Awake() { if (!KASAPI.isLoaded) { KASAPI.JointUtils = new KASImpl.JointUtilsImpl(); KASAPI.AttachNodesUtils = new KASImpl.AttachNodesUtilsImpl(); KASAPI.LinkUtils = new KASImpl.LinkUtilsImpl(); KASAPI.PhysicsUtils = new KASImpl.PhysicsUtilsImpl(); KASAPI.CommonConfig = new KASImpl.CommonConfigImpl(); KASAPI.KasEvents = new KASImpl.KasEventsImpl(); KASAPI.isLoaded = true; var assembly = GetType().Assembly; DebugEx.Info("Loading KAS API v2 from: {0} (v{1})", KspPaths.MakeRelativePathToGameData(assembly.Location), assembly.GetName().Version); } }
/// <summary> /// Writes persistent fields into the config files specified by the class annotation. /// </summary> /// <remarks>Method updates the config file(s) by preserving top level nodes that are not /// specified as targets for the requested group. /// <para>Note, that fields cannot be writtent into database. Such annotations will be skipped /// during the save.</para> /// </remarks> /// <param name="type">A type to write fields for.</param> /// <param name="instance">An instance of type <paramref name="type"/>. If it's <c>null</c> then /// only static fields will be written.</param> /// <param name="group">A group to write fields for. If <c>null</c> then all groups that are /// defined in the class annotation via <see cref="PersistentFieldsFileAttribute"/> will be /// written.</param> /// <seealso cref="PersistentFieldAttribute"/> /// <seealso cref="PersistentFieldsFileAttribute"/> public static void WriteFieldsFromType(Type type, object instance, string group = StdPersistentGroups.Default) { var attributes = GetPersistentFieldsFiles(type, group); DebugEx.Fine("Writing persistent fields: type={0}, group=\"{1}\"", type, group ?? "<ALL>"); foreach (var attr in attributes) { if (attr.configFilePath.Length > 0) { WriteFieldsIntoFile(KspPaths.MakeAbsPathForGameData(attr.configFilePath), type, instance, rootNodePath: attr.nodePath, mergeMode: true, group: attr.group); } else { DebugEx.Fine("Not saving database group: {0}", attr.nodePath); } } }
/// <summary> /// Patches the part configs so that they refer the tags for the localizable fileds, and saves the /// modified fiels in the export location. /// </summary> /// <remarks></remarks> /// <param name="parts">The parts to patch.</param> void GuiExportPartConfigs(IEnumerable <PartsRecord> parts) { var exportParts = parts.SelectMany(x => x.parts); var exportPath = KspPaths.GetModsDataFilePath(this, "Parts/"); foreach (var part in exportParts) { var config = ConfigStore.LoadConfigWithComments( part.configFileFullName, localizeValues: false); if (config == null) { Debug.LogErrorFormat( "Cannot load config file for part {0}: {1}", part.name, part.configFileFullName); continue; } var partNode = config.GetNode("PART"); foreach (var fieldName in Extractor.localizablePartFields) { var field = partNode.values.Cast <ConfigNode.Value>() .FirstOrDefault(x => x.name == fieldName); if (field == null) { Debug.LogWarningFormat("Field '{0}' is not found in the part {1} config", fieldName, part.name); continue; } if (field.value.StartsWith("#", StringComparison.Ordinal)) { continue; // It's already localized. } var locTag = Extractor.MakePartFieldLocalizationTag(part.name, fieldName); field.comment = locTag + " = " + field.value; field.value = locTag; } var tgtPath = exportPath + part.name.Replace(".", "_") + ".cfg"; Debug.LogWarningFormat("Saving patched part config into: {0}", tgtPath); ConfigStore.SaveConfigWithComments(config, tgtPath); } ShowCompletionDialog( ConfigSavedDlgTitle, ConfigsSavedInFolderTxt.Format(exportParts.Count(), exportPath)); }
void Awake() { if (loaded) { gameObject.DestroyGameObject(); return; // Only let the loader to work once per version. } loaded = true; var assembly = GetType().Assembly; assemblyVersionStr = $"{KspPaths.MakeRelativePathToGameData(assembly.Location)} (v{assembly.GetName().Version})"; DebugEx.Info("Loading KSPDevUtils: {0}", assemblyVersionStr); // Install the localization callbacks. The object must not be destroyed. DontDestroyOnLoad(gameObject); gameObject.AddComponent <LocalizationLoader>(); gameObject.AddComponent <UISoundPlayer>(); }
/// <summary>Writes values of the annotated persistent fields into a file.</summary> /// <remarks> /// All persitent values are <b>added</b> into the file provided. I.e. if node had already had a /// value being persited then it either overwritten (ordinary fields) or extended (collection /// fields). /// </remarks> /// <param name="filePath"> /// A relative or an absolute path to the file. It's resolved via /// <see cref="KspPaths.MakeAbsPathForGameData"/>. /// </param> /// <param name="rootNodePath"> /// A path to the node in the file where the data should be written. If the node already exsists /// it will be deleted. /// </param> /// <param name="type">A type to write fields for.</param> /// <param name="instance"> /// An instance of type <paramref name="type"/>. If it's <c>null</c> then only static fields will /// be written. /// </param> /// <param name="mergeMode"> /// If <c>true</c> and the file already exists then only will be created. /// </param> /// <param name="group">A group tag (see <see cref="BasePersistentFieldAttribute"/>).</param> /// <seealso cref="PersistentFieldAttribute"/> public static void WriteFieldsIntoFile(string filePath, Type type, object instance, string rootNodePath = null, bool mergeMode = true, string group = StdPersistentGroups.Default) { DebugEx.Fine("Writing persistent fields: file={0}, group=\"{1}\", isMerging={2}, root={3}", KspPaths.MakeRelativePathToGameData(filePath), group ?? "<ALL>", mergeMode, rootNodePath ?? "/"); var node = mergeMode ? ConfigNode.Load(filePath) ?? new ConfigNode() // Make empty node if file doesn't exist. : new ConfigNode(); var tagetNode = node; if (rootNodePath != null) { tagetNode = GetNodeByPath(node, rootNodePath, createIfMissing: true); tagetNode.ClearData(); // In case of it's an existing node. } WriteFieldsIntoNode(tagetNode, type, instance, group: group); node.Save(KspPaths.MakeAbsPathForGameData(filePath)); }
/// <summary>Saves the strings for the selected entities into a new file.</summary> /// <param name="parts">The parts to export the strings from.</param> /// <param name="assemblies">The mod assemblies to export teh strinsg from.</param> void GuiActionExportStrings(IEnumerable <PartsRecord> parts, IEnumerable <AssemblyRecord> assemblies) { var partsLocs = parts .SelectMany(x => x.parts) .Select(Extractor.EmitItemsForPart) .SelectMany(x => x) .ToList(); var modulesLocs = assemblies .SelectMany(x => x.assembly.GetTypes()) .Select(Extractor.EmitItemsForType) .SelectMany(x => x) .ToList(); Debug.LogWarningFormat("Export {0} parts strings and {1} modules strings", partsLocs.Count, modulesLocs.Count); var locItems = partsLocs.Union(modulesLocs); var filename = KspPaths.GetModsDataFilePath(this, "export.cfg", createMissingDirs: true); ConfigStore.WriteLocItems(locItems, Localizer.CurrentLanguage, filename); Debug.LogWarningFormat("Strings are written into: {0}", filename); }
/// <summary>Finds all the entities for the prefix, and populates the list.</summary> /// <param name="prefix">The prefix to find URL by.</param> void GuiActionUpdateTargets(string prefix) { targets.Clear(); if (prefix.Length < 3) { targets.Add(new StubRecord() { stubText = TypePrefixToStartTxt, }); return; } // Find part configs for the prefix. targets.AddRange(PartLoader.LoadedPartsList .Where(x => x.partUrl.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) .OrderBy(x => x.partUrl) .GroupBy(x => { var pos = x.partUrl.LastIndexOf("/Parts", StringComparison.OrdinalIgnoreCase); return(pos != -1 ? x.partUrl.Substring(0, pos + 6) : x.partUrl.Split('/')[0]); }) .Select(group => new PartsRecord() { urlPrefix = group.Key, parts = group.ToList(), }) .Cast <ScannedRecord>()); // Find assemblies for the prefix. // Utility assemblies of the same version are loaded only once, but they are referred for every // URL at which the assembly was found. targets.AddRange(AssemblyLoader.loadedAssemblies .Where(x => x.url.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) && KspPaths.MakeRelativePathToGameData(x.assembly.Location) .StartsWith(prefix, StringComparison.OrdinalIgnoreCase) && (allowNoModulesAssemblies || x.types.Count > 0)) .Select(assembly => new AssemblyRecord() { assembly = assembly.assembly, types = assembly.types.SelectMany(x => x.Value).ToList(), url = assembly.url, }) .Cast <ScannedRecord>()); // Find localization files for the prefix. targets.AddRange(GameDatabase.Instance.GetConfigs("Localization") .Where(x => x.url.StartsWith(lookupPrefix, StringComparison.OrdinalIgnoreCase) && x.config.GetNodes(Localizer.CurrentLanguage).Any()) .Select(url => new ConfigRecord() { url = url.url, filePath = url.parent.fullPath, lang = Localizer.CurrentLanguage, node = url.config.GetNodes(Localizer.CurrentLanguage).FirstOrDefault(), }) .Cast <ScannedRecord>()); if (targets.Count == 0) { targets.Add(new StubRecord() { stubText = NothingFoundForPrefixTxt, }); } }
/// <inheritdoc/> public override string ToString() { return(string.Format( "{0}, lang={1} ({2} strings)", KspPaths.MakeRelativePathToGameData(url), lang, node.GetValues().Length)); }
/// <inheritdoc/> public override string ToString() { return(string.Format("{0}, v{1} ({2} modules)", KspPaths.MakeRelativePathToGameData(assembly.Location), assembly.GetName().Version, types.Count)); }
/// <inheritdoc/> public override string ToString() { return ConfigRecordTxt.Format( KspPaths.MakeRelativePathToGameData(url), lang, node.GetValues().Length); }
/// <inheritdoc/> public override string ToString() { return AssemblyRecordTxt.Format( KspPaths.MakeRelativePathToGameData(assembly.Location), assembly.GetName().Version.ToString(), types.Count); }