Example #1
0
        /// <summary>Reads part's config file from file for the requested part.</summary>
        /// <remarks>
        /// It loads the config with the inline comments. For the multi-part configs the right node is
        /// located. If the config has MM patches for the "PART" section, then they are dropped, but a
        /// warning is logged.
        /// </remarks>
        /// <param name="partInfo">The part to get config for.</param>
        /// <returns>The config or <c>null</c> if config cannot be found or loaded.</returns>
        static ConfigNode GetPartPrefabConfig(AvailablePart partInfo)
        {
            if (string.IsNullOrEmpty(partInfo.configFileFullName))
            {
                DebugEx.Error("Skip part {0} since it doesn't have a config", partInfo.name);
                return(null);
            }

            var config = ConfigStore.LoadConfigWithComments(partInfo.configFileFullName);

            if (config == null || config.nodes.Count == 0)
            {
                DebugEx.Error("Config node is invalid for the part {0}\n{1}", partInfo.name, config);
                return(null);
            }
            ConfigNode result = null;

            for (var i = 0; i < config.nodes.Count; i++)
            {
                var node = config.nodes[i];
                if (node.name != "PART")
                {
                    DebugEx.Warning("Non-part node in config of part {0}: {1}", partInfo.name, node.name);
                    continue;
                }
                // KSP mangles with the part names translating "." to "_" and back. So just in case do it on
                // the both sides.
                if (node.GetValue("name").Replace(".", "_") != partInfo.name.Replace(".", "_"))
                {
                    DebugEx.Warning("Node in config of part '{0}' doesn't match the part name: '{1}'",
                                    partInfo.name, node.GetValue("name"));
                    continue;
                }
                if (result == null)
                {
                    result = node;
                }
                else
                {
                    DebugEx.Warning(
                        "Skipping node #{0} in config of part {1} due to duplication", i, partInfo.name);
                }
            }
            return(result);
        }
Example #2
0
        /// <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));
        }
        /// <summary>Extracts the localization items from the part's config.</summary>
        /// <param name="part">The part to extract items for.</param>
        /// <returns>All the localization items for the part.</returns>
        public static List <LocItem> EmitItemsForPart(AvailablePart part)
        {
            var res = new List <LocItem>();
            // The part's config in the AvailablePart doesn't have all the fields and lacks the comments.
            // We do need the comments to resolve the field tag names, so load via a custom method.
            // In case of the custom loading method fails, use the stock one without the comments support.
            var config = (ConfigStore.LoadConfigWithComments(part.configFileFullName)
                          ?? ConfigNode.Load(part.configFileFullName)).GetNode("PART");

            if (config == null)
            {
                DebugEx.Error("Failed to load part's config: partName={0}, configUrl={1}",
                              part.name, part.configFileFullName);
                return(res);
            }

            // Go through the fields we know must be localized.
            foreach (var fieldName in LocalizablePartFields)
            {
                var field = config.values.Cast <ConfigNode.Value>().FirstOrDefault(x => x.name == fieldName);
                if (field == null)
                {
                    DebugEx.Warning("Field '{0}' is not found in the part {1} config", fieldName, part.name);
                    continue;
                }
                config.values.Remove(field); // Don't handle it down the stream.
                string locTag          = null;
                string locDefaultValue = null;
                var    meta            = MetaBlock.MakeFromString(field.comment);
                if (LocalizationManager.IsLocalizationTag(meta.inlineComment, firstWordOnly: true))
                {
                    var match = Regex.Match(meta.inlineComment, @"^(#[a-zA-Z0-9_-]+)\s*=\s*(.+?)$");
                    if (match.Success)
                    {
                        locTag = match.Groups[1].Value;
                        if (field.value == locTag)
                        {
                            // Part's tag localization failed, use the default text.
                            locDefaultValue = match.Groups[2].Value;
                        }
                    }
                    else
                    {
                        DebugEx.Warning("Cannot resolve default localization tag in field {0} for part {1}: {2}",
                                        fieldName, config.GetValue("name"), meta.inlineComment);
                    }
                }
                // ReSharper disable once UseStringInterpolation
                var fieldOrderStr = string.Format(
                    "\0x00_{0:000}_{1}", // Use zero prefix to place it before anything else.
                    Controller.partFieldsSorting.IndexOf(fieldName, StringComparison.Ordinal),
                    fieldName);
                var item = new LocItem()
                {
                    groupKey        = "Part: " + part.name,
                    sortKey         = fieldOrderStr,
                    fullFilePath    = part.configFileFullName,
                    locTag          = locTag ?? MakePartFieldLocalizationTag(config.GetValue("name"), fieldName),
                    locDefaultValue = locDefaultValue ?? field.value,
                };
                res.Add(item);
            }

            res.AddRange(EmitItemsForNode(part, config));

            return(res);
        }
Example #4
0
        /// <summary>Extracts the localization items from the part's config.</summary>
        /// <param name="part">The part to extract items for.</param>
        /// <returns>All the localization items for the part.</returns>
        public static List <LocItem> EmitItemsForPart(AvailablePart part)
        {
            var res = new List <LocItem>();
            // The part's config in the AvailablePart doesn't have all the fields and lacks the comments.
            // We do need the comments to resolve the field tag names, so load via a custom method.
            // In case of the custom loading method fails, use the stock one without the comments support.
            var config = (ConfigStore.LoadConfigWithComments(part.configFileFullName)
                          ?? ConfigNode.Load(part.configFileFullName)).GetNode("PART");

            if (config == null)
            {
                Debug.LogErrorFormat("Failed to load part's config: partName={0}, configUrl={1}",
                                     part.name, part.configFileFullName);
                return(res);
            }

            // Go thru the fields we know must be localized.
            foreach (var fieldName in localizablePartFields)
            {
                var field = config.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;
                }
                string locTag          = null;
                string locDefaultValue = null;
                if (!string.IsNullOrEmpty(field.comment) &&
                    field.comment.StartsWith("#", StringComparison.Ordinal))
                {
                    var match = Regex.Match(field.comment, @"^(#[a-zA-Z0-9_-]+)\s*=\s*(.+?)$");
                    if (match.Success)
                    {
                        locTag = match.Groups[1].Value;
                        if (field.value == locTag)
                        {
                            // Part's tag localization failed, use the default text.
                            locDefaultValue = match.Groups[2].Value;
                        }
                    }
                    else
                    {
                        Debug.LogWarningFormat(
                            "Cannot resolve defult localization tag in field {0} for part {1}: {2}",
                            fieldName, config.GetValue("name"), field.comment);
                    }
                }
                var item = new LocItem()
                {
                    groupKey        = "Part: " + part.name,
                    fullFilePath    = part.configFileFullName,
                    locTag          = locTag ?? MakePartFieldLocalizationTag(config.GetValue("name"), fieldName),
                    locDefaultValue = locDefaultValue ?? field.value,
                };
                res.Add(item);
            }

            res.AddRange(EmitItemsForNode(part, config));

            return(res);
        }