/// <summary>
        /// Generates a character cache, which prevents us from repeatedly loading XmlNodes or caching a full character.
        /// </summary>
        /// <param name="strFile"></param>
        private async Task <TreeNode> CacheCharacters(string strFile)
        {
            if (!File.Exists(strFile))
            {
                Program.ShowMessageBox(
                    this,
                    string.Format(GlobalSettings.CultureInfo,
                                  await LanguageManager.GetStringAsync("Message_File_Cannot_Be_Accessed"), strFile));
                return(null);
            }

            ThreadSafeList <XPathNavigator> lstCharacterXmlStatblocks = new ThreadSafeList <XPathNavigator>(3);

            try
            {
                try
                {
                    using (ZipArchive zipArchive
                               = ZipFile.Open(strFile, ZipArchiveMode.Read, Encoding.GetEncoding(850)))
                    {
                        // NOTE: Cannot parallelize because ZipFile.Open creates one handle on the entire zip file that gets messed up if we try to get it to read multiple files at once
                        foreach (ZipArchiveEntry entry in zipArchive.Entries)
                        {
                            string strEntryFullName = entry.FullName;
                            if (strEntryFullName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase) &&
                                strEntryFullName.StartsWith("statblocks_xml", StringComparison.Ordinal))
                            {
                                // If we run into any problems loading the character cache, fail out early.
                                try
                                {
                                    using (StreamReader sr = new StreamReader(entry.Open(), true))
                                    {
                                        await Task.Run(() =>
                                        {
                                            XPathDocument xmlSourceDoc;
                                            using (XmlReader objXmlReader
                                                       = XmlReader.Create(sr, GlobalSettings.SafeXmlReaderSettings))
                                                xmlSourceDoc = new XPathDocument(objXmlReader);
                                            XPathNavigator objToAdd = xmlSourceDoc.CreateNavigator();
                                            lstCharacterXmlStatblocks.Add(objToAdd);
                                        });
                                    }
                                }
                                // If we run into any problems loading the character cache, fail out early.
                                catch (IOException)
                                {
                                    Utils.BreakIfDebug();
                                }
                                catch (XmlException)
                                {
                                    Utils.BreakIfDebug();
                                }
                            }
                            else if (strEntryFullName.StartsWith("images", StringComparison.Ordinal) &&
                                     strEntryFullName.Contains('.'))
                            {
                                string strKey = Path.GetFileName(strEntryFullName);
                                using (Bitmap bmpMugshot = new Bitmap(entry.Open(), true))
                                {
                                    Bitmap bmpNewMugshot = bmpMugshot.PixelFormat == PixelFormat.Format32bppPArgb
                                        ? bmpMugshot.Clone() as Bitmap // Clone makes sure file handle is closed
                                        : bmpMugshot.ConvertPixelFormat(PixelFormat.Format32bppPArgb);
                                    while (!await _dicImages.TryAddAsync(strKey, bmpNewMugshot))
                                    {
                                        (bool blnSuccess, Bitmap bmpOldMugshot) =
                                            await _dicImages.TryRemoveAsync(strKey);

                                        if (blnSuccess)
                                        {
                                            bmpOldMugshot?.Dispose();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                catch (IOException)
                {
                    Program.ShowMessageBox(
                        this,
                        string.Format(GlobalSettings.CultureInfo,
                                      await LanguageManager.GetStringAsync("Message_File_Cannot_Be_Accessed"),
                                      strFile));
                    return(null);
                }
                catch (NotSupportedException)
                {
                    Program.ShowMessageBox(
                        this,
                        string.Format(GlobalSettings.CultureInfo,
                                      await LanguageManager.GetStringAsync("Message_File_Cannot_Be_Accessed"),
                                      strFile));
                    return(null);
                }
                catch (UnauthorizedAccessException)
                {
                    Program.ShowMessageBox(
                        this, await LanguageManager.GetStringAsync("Message_Insufficient_Permissions_Warning"));
                    return(null);
                }

                string strFileText
                    = await strFile.CheapReplaceAsync(Utils.GetStartupPath, () => '<' + Application.ProductName + '>');

                TreeNode nodRootNode = new TreeNode
                {
                    Text        = strFileText,
                    ToolTipText = strFileText
                };

                XPathNavigator xmlMetatypesDocument = await XmlManager.LoadXPathAsync("metatypes.xml");

                foreach (XPathNavigator xmlCharacterDocument in lstCharacterXmlStatblocks)
                {
                    XPathNavigator xmlBaseCharacterNode
                        = xmlCharacterDocument.SelectSingleNode("/document/public/character");
                    if (xmlBaseCharacterNode != null)
                    {
                        HeroLabCharacterCache objCache = new HeroLabCharacterCache
                        {
                            PlayerName = xmlBaseCharacterNode.SelectSingleNode("@playername")?.Value ?? string.Empty
                        };
                        string strNameString = xmlBaseCharacterNode.SelectSingleNode("@name")?.Value ?? string.Empty;
                        objCache.CharacterId = strNameString;
                        if (!string.IsNullOrEmpty(strNameString))
                        {
                            int intAsIndex = strNameString.IndexOf(" as ", StringComparison.Ordinal);
                            if (intAsIndex != -1)
                            {
                                objCache.CharacterName = strNameString.Substring(0, intAsIndex);
                                objCache.CharacterAlias
                                    = strNameString.Substring(intAsIndex).TrimStart(" as ").Trim('\'');
                            }
                            else
                            {
                                objCache.CharacterName = strNameString;
                            }
                        }

                        string strRaceString = xmlBaseCharacterNode.SelectSingleNode("race/@name")?.Value;
                        if (strRaceString == "Metasapient")
                        {
                            strRaceString = "A.I.";
                        }
                        if (!string.IsNullOrEmpty(strRaceString))
                        {
                            foreach (XPathNavigator xmlMetatype in xmlMetatypesDocument.Select(
                                         "/chummer/metatypes/metatype"))
                            {
                                string strMetatypeName = xmlMetatype.SelectSingleNode("name")?.Value ?? string.Empty;
                                if (strMetatypeName == strRaceString)
                                {
                                    objCache.Metatype    = strMetatypeName;
                                    objCache.Metavariant = "None";
                                    break;
                                }

                                foreach (XPathNavigator xmlMetavariant in
                                         await xmlMetatype.SelectAndCacheExpressionAsync("metavariants/metavariant"))
                                {
                                    string strMetavariantName
                                        = xmlMetavariant.SelectSingleNode("name")?.Value ?? string.Empty;
                                    if (strMetavariantName == strRaceString)
                                    {
                                        objCache.Metatype    = strMetatypeName;
                                        objCache.Metavariant = strMetavariantName;
                                        break;
                                    }
                                }
                            }
                        }

                        objCache.Description = xmlBaseCharacterNode.SelectSingleNode("personal/description")?.Value;
                        objCache.Karma       = xmlBaseCharacterNode.SelectSingleNode("karma/@total")?.Value ?? "0";
                        objCache.Essence     = xmlBaseCharacterNode
                                               .SelectSingleNode("attributes/attribute[@name = \"Essence\"]/@text")?.Value;
                        objCache.BuildMethod
                            = xmlBaseCharacterNode.SelectSingleNode("creation/bp/@total")?.ValueAsInt <= 100
                                ? CharacterBuildMethod.Priority
                                : CharacterBuildMethod.Karma;

                        string strSettingsSummary =
                            xmlBaseCharacterNode.SelectSingleNode("settings/@summary")?.Value;
                        if (!string.IsNullOrEmpty(strSettingsSummary))
                        {
                            int  intSemicolonIndex;
                            bool blnDoFullHouse = false;
                            int  intSourcebooksIndex
                                = strSettingsSummary.IndexOf("Core Rulebooks:", StringComparison.OrdinalIgnoreCase);
                            if (intSourcebooksIndex != -1)
                            {
                                intSemicolonIndex = strSettingsSummary.IndexOf(';', intSourcebooksIndex);
                                if (intSourcebooksIndex + 16 < intSemicolonIndex)
                                {
                                    blnDoFullHouse
                                        = true; // We probably have multiple books enabled, so use Full House instead
                                }
                            }

                            string strHeroLabSettingsName = "Standard";

                            int intCharCreationSystemsIndex =
                                strSettingsSummary.IndexOf("Character Creation Systems:",
                                                           StringComparison.OrdinalIgnoreCase);
                            if (intCharCreationSystemsIndex != -1)
                            {
                                intSemicolonIndex = strSettingsSummary.IndexOf(';', intCharCreationSystemsIndex);
                                if (intCharCreationSystemsIndex + 28 <= intSemicolonIndex)
                                {
                                    strHeroLabSettingsName = strSettingsSummary.Substring(
                                        intCharCreationSystemsIndex + 28,
                                        strSettingsSummary.IndexOf(
                                            ';', intCharCreationSystemsIndex)
                                        - 28 - intCharCreationSystemsIndex)
                                                             .Trim();
                                    if (strHeroLabSettingsName == "Established Runners")
                                    {
                                        strHeroLabSettingsName = "Standard";
                                    }
                                }
                            }

                            if (strHeroLabSettingsName == "Standard")
                            {
                                if (blnDoFullHouse)
                                {
                                    strHeroLabSettingsName = objCache.BuildMethod == CharacterBuildMethod.Karma
                                        ? "Full House (Point Buy)"
                                        : "Full House";
                                }
                                else if (objCache.BuildMethod == CharacterBuildMethod.Karma)
                                {
                                    strHeroLabSettingsName = "Point Buy";
                                }
                            }

                            objCache.SettingsName = strHeroLabSettingsName;
                        }

                        objCache.Created = objCache.Karma != "0";
                        if (!objCache.Created)
                        {
                            XPathNodeIterator xmlJournalEntries
                                = await xmlBaseCharacterNode.SelectAndCacheExpressionAsync("journals/journal");

                            if (xmlJournalEntries?.Count > 1)
                            {
                                objCache.Created = true;
                            }
                            else if (xmlJournalEntries?.Count == 1 &&
                                     xmlJournalEntries.Current?.SelectSingleNode("@name")?.Value != "Title")
                            {
                                objCache.Created = true;
                            }
                        }

                        string strImageString = xmlBaseCharacterNode.SelectSingleNode("images/image/@filename")?.Value;
                        if (!string.IsNullOrEmpty(strImageString))
                        {
                            (bool blnSuccess, Bitmap objTemp) = await _dicImages.TryGetValueAsync(strImageString);

                            if (blnSuccess)
                            {
                                objCache.Mugshot = objTemp;
                            }
                        }

                        objCache.FilePath = strFile;
                        TreeNode objNode = new TreeNode
                        {
                            Text        = await CalculatedName(objCache),
                            ToolTipText = await strFile.CheapReplaceAsync(Utils.GetStartupPath,
                                                                          () => '<' + Application.ProductName + '>')
                        };
                        nodRootNode.Nodes.Add(objNode);

                        await _lstCharacterCache.AddAsync(objCache);

                        objNode.Tag = await _lstCharacterCache.IndexOfAsync(objCache);
                    }
                }

                nodRootNode.Expand();
                return(nodRootNode);
            }
            finally
            {
                await lstCharacterXmlStatblocks.DisposeAsync();
            }
        }
Beispiel #2
0
        /// <summary>
        /// Generates a character cache, which prevents us from repeatedly loading XmlNodes or caching a full character.
        /// </summary>
        /// <param name="strFile"></param>
        private TreeNode CacheCharacters(string strFile)
        {
            if (!File.Exists(strFile))
            {
                Program.MainForm.ShowMessageBox(
                    this,
                    string.Format(GlobalSettings.CultureInfo,
                                  LanguageManager.GetString("Message_File_Cannot_Be_Accessed"), strFile));
                return(null);
            }

            using (ThreadSafeList <XPathNavigator> lstCharacterXmlStatblocks = new ThreadSafeList <XPathNavigator>(3))
            {
                try
                {
                    using (ZipArchive zipArchive
                               = ZipFile.Open(strFile, ZipArchiveMode.Read, Encoding.GetEncoding(850)))
                    {
                        Parallel.ForEach(zipArchive.Entries, entry =>
                        {
                            string strEntryFullName = entry.FullName;
                            if (strEntryFullName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase) &&
                                strEntryFullName.StartsWith("statblocks_xml", StringComparison.Ordinal))
                            {
                                // If we run into any problems loading the character cache, fail out early.
                                try
                                {
                                    XPathDocument xmlSourceDoc;
                                    using (StreamReader sr = new StreamReader(entry.Open(), true))
                                        using (XmlReader objXmlReader
                                                   = XmlReader.Create(sr, GlobalSettings.SafeXmlReaderSettings))
                                            xmlSourceDoc = new XPathDocument(objXmlReader);
                                    XPathNavigator objToAdd = xmlSourceDoc.CreateNavigator();
                                    // ReSharper disable once AccessToDisposedClosure
                                    lstCharacterXmlStatblocks.Add(objToAdd);
                                }
                                // If we run into any problems loading the character cache, fail out early.
                                catch (IOException)
                                {
                                    Utils.BreakIfDebug();
                                }
                                catch (XmlException)
                                {
                                    Utils.BreakIfDebug();
                                }
                            }
                            else if (strEntryFullName.StartsWith("images", StringComparison.Ordinal) &&
                                     strEntryFullName.Contains('.'))
                            {
                                string strKey = Path.GetFileName(strEntryFullName);
                                using (Bitmap bmpMugshot = new Bitmap(entry.Open(), true))
                                {
                                    Bitmap bmpNewMugshot = bmpMugshot.PixelFormat == PixelFormat.Format32bppPArgb
                                        ? bmpMugshot.Clone() as Bitmap // Clone makes sure file handle is closed
                                        : bmpMugshot.ConvertPixelFormat(PixelFormat.Format32bppPArgb);
                                    while (!_dicImages.TryAdd(strKey, bmpNewMugshot))
                                    {
                                        if (_dicImages.TryRemove(strKey, out Bitmap bmpOldMugshot))
                                        {
                                            bmpOldMugshot?.Dispose();
                                        }
                                    }
                                }
                            }
                        });
                    }
                }
                catch (IOException)
                {
                    Program.MainForm.ShowMessageBox(
                        this,
                        string.Format(GlobalSettings.CultureInfo,
                                      LanguageManager.GetString("Message_File_Cannot_Be_Accessed"), strFile));
                    return(null);
                }
                catch (NotSupportedException)
                {
                    Program.MainForm.ShowMessageBox(
                        this,
                        string.Format(GlobalSettings.CultureInfo,
                                      LanguageManager.GetString("Message_File_Cannot_Be_Accessed"), strFile));
                    return(null);
                }
                catch (UnauthorizedAccessException)
                {
                    Program.MainForm.ShowMessageBox(
                        this, LanguageManager.GetString("Message_Insufficient_Permissions_Warning"));
                    return(null);
                }

                string strFileText
                    = strFile.CheapReplace(Utils.GetStartupPath, () => '<' + Application.ProductName + '>');
                TreeNode nodRootNode = new TreeNode
                {
                    Text        = strFileText,
                    ToolTipText = strFileText
                };

                XPathNavigator xmlMetatypesDocument = XmlManager.LoadXPath("metatypes.xml");
                foreach (XPathNavigator xmlCharacterDocument in lstCharacterXmlStatblocks)
                {
                    XPathNavigator xmlBaseCharacterNode
                        = xmlCharacterDocument.SelectSingleNode("/document/public/character");
                    if (xmlBaseCharacterNode != null)
                    {
                        HeroLabCharacterCache objCache = new HeroLabCharacterCache
                        {
                            PlayerName = xmlBaseCharacterNode.SelectSingleNode("@playername")?.Value ?? string.Empty
                        };
                        string strNameString = xmlBaseCharacterNode.SelectSingleNode("@name")?.Value ?? string.Empty;
                        objCache.CharacterId = strNameString;
                        if (!string.IsNullOrEmpty(strNameString))
                        {
                            int intAsIndex = strNameString.IndexOf(" as ", StringComparison.Ordinal);
                            if (intAsIndex != -1)
                            {
                                objCache.CharacterName = strNameString.Substring(0, intAsIndex);
                                objCache.CharacterAlias
                                    = strNameString.Substring(intAsIndex).TrimStart(" as ").Trim('\'');
                            }
                            else
                            {
                                objCache.CharacterName = strNameString;
                            }
                        }

                        string strRaceString = xmlBaseCharacterNode.SelectSingleNode("race/@name")?.Value;
                        if (strRaceString == "Metasapient")
                        {
                            strRaceString = "A.I.";
                        }
                        if (!string.IsNullOrEmpty(strRaceString))
                        {
                            foreach (XPathNavigator xmlMetatype in xmlMetatypesDocument.Select(
                                         "/chummer/metatypes/metatype"))
                            {
                                string strMetatypeName = xmlMetatype.SelectSingleNode("name")?.Value ?? string.Empty;
                                if (strMetatypeName == strRaceString)
                                {
                                    objCache.Metatype    = strMetatypeName;
                                    objCache.Metavariant = "None";
                                    break;
                                }

                                foreach (XPathNavigator xmlMetavariant in
                                         xmlMetatype.SelectAndCacheExpression("metavariants/metavariant"))
                                {
                                    string strMetavariantName
                                        = xmlMetavariant.SelectSingleNode("name")?.Value ?? string.Empty;
                                    if (strMetavariantName == strRaceString)
                                    {
                                        objCache.Metatype    = strMetatypeName;
                                        objCache.Metavariant = strMetavariantName;
                                        break;
                                    }
                                }
                            }
                        }

                        objCache.Description = xmlBaseCharacterNode.SelectSingleNode("personal/description")?.Value;
                        objCache.Karma       = xmlBaseCharacterNode.SelectSingleNode("karma/@total")?.Value ?? "0";
                        objCache.Essence     = xmlBaseCharacterNode
                                               .SelectSingleNode("attributes/attribute[@name = \"Essence\"]/@text")?.Value;
                        objCache.BuildMethod
                            = xmlBaseCharacterNode.SelectSingleNode("creation/bp/@total")?.Value == "25"
                                ? nameof(CharacterBuildMethod.Priority)
                                : nameof(CharacterBuildMethod.Karma);

                        objCache.Created = objCache.Karma != "0";
                        if (!objCache.Created)
                        {
                            XPathNodeIterator xmlJournalEntries = xmlBaseCharacterNode.SelectAndCacheExpression("journals/journal");
                            if (xmlJournalEntries?.Count > 1)
                            {
                                objCache.Created = true;
                            }
                            else if (xmlJournalEntries?.Count == 1 &&
                                     xmlJournalEntries.Current?.SelectSingleNode("@name")?.Value != "Title")
                            {
                                objCache.Created = true;
                            }
                        }

                        string strImageString = xmlBaseCharacterNode.SelectSingleNode("images/image/@filename")?.Value;
                        if (!string.IsNullOrEmpty(strImageString) &&
                            _dicImages.TryGetValue(strImageString, out Bitmap objTemp))
                        {
                            objCache.Mugshot = objTemp;
                        }

                        objCache.FilePath = strFile;
                        TreeNode objNode = new TreeNode
                        {
                            Text        = CalculatedName(objCache),
                            ToolTipText = strFile.CheapReplace(Utils.GetStartupPath,
                                                               () => '<' + Application.ProductName + '>')
                        };
                        nodRootNode.Nodes.Add(objNode);

                        _lstCharacterCache.Add(objCache);
                        objNode.Tag = _lstCharacterCache.IndexOf(objCache);
                    }
                }

                nodRootNode.Expand();
                return(nodRootNode);
            }
        }