private void RawExtract()
        {
            var Missing             = "".Select(t => new { abnormalid = string.Empty, isShow = string.Empty, num = 0, redirected = string.Empty }).ToList();
            var Dots                = "".Select(t => new { abnormalid = string.Empty, type = string.Empty, amount = string.Empty, method = string.Empty, time = string.Empty, tick = string.Empty, num = 0, property = string.Empty, isBuff = string.Empty, isShow = string.Empty }).ToList();
            var interesting         = new string[] { "1", "3", "4", "5", "6", "7", "8", "9", "18", "19", "36", "20", "22", "24", "25", "27", "28", "30", "103", "104", "105", "108", "162", "167", "168", "203", "207", "210", "208", "221", "227", "229", "231", "235", "236", "237", "249", "255", "260", "283", "316" };
            var notinteresting      = new string[] { "8", "9", "18", "20", "27", "28", "103", "105", "108", "168", "221", "227" };
            var redirects_to_ignore = new string[] { "161", "182", "252", "264" };
            var redirects_to_follow = new string[] { "64", "161", "182", "223", "248", "252", "264", "271" };

            foreach (
                var file in
                Directory.EnumerateFiles(RootFolder + _region + "/Abnormality/"))
            {
                var xml     = XDocument.Load(file);
                var Dotdata = (from item in xml.Root.Elements("Abnormal")
                               let abnormalid = item.Attribute("id").Value
                                                let isShow = item.Attribute("isShow") == null ? "False" : item.Attribute("isShow").Value
                                                             let property = item.Attribute("property") == null ? "0" : item.Attribute("property").Value
                                                                            let isBuff = item.Attribute("isBuff") == null ? "True" : item.Attribute("isBuff").Value
                                                                                         let time = item.Attribute("infinity").Value == "True" ? "0" : item.Attribute("time").Value
                                                                                                    from eff in item.Elements("AbnormalityEffect")
                                                                                                    let type = eff.Attribute("type") == null ? "0" : eff.Attribute("type").Value
                                                                                                               let method = eff.Attribute("method") == null ? "" : eff.Attribute("method").Value
                                                                                                                            let num = item.Elements("AbnormalityEffect").TakeWhile(x => x != eff).Count() + 1
                                                                                                                                      let amount = eff.Attribute("value") == null ? "" : eff.Attribute("value").Value
                                                                                                                                                   let tick = eff.Attribute("tickInterval") == null ? "0" : eff.Attribute("tickInterval").Value
                                                                                                                                                              where (((type == "51" || type == "52") && tick != "0") || (!redirects_to_ignore.Contains(type) && (isShow != "False" || abnormalid == "201" || abnormalid == "202" || abnormalid == "10152050"))) && amount != "" && method != ""
                                                                                                                                                              select new { abnormalid, type, amount, method, time, tick, num, property, isBuff, isShow }).ToList(); //// 201 202 - marked as not shown, but needed
                Dots = Dots.Union(Dotdata).ToList();
                var parser = (from item in xml.Root.Elements("Abnormal")
                              let abnormalid = item.Attribute("id").Value
                                               let isShow = item.Attribute("isShow") == null ? "False" : item.Attribute("isShow").Value
                                                            from eff in item.Elements("AbnormalityEffect")
                                                            let type = eff.Attribute("type") == null ? "0" : eff.Attribute("type").Value
                                                                       let num = item.Elements("AbnormalityEffect").TakeWhile(x => x != eff).Count() + 1
                                                                                 let redirected = eff.Attribute("value") == null ? "" : eff.Attribute("value").Value
                                                                                                  where redirects_to_follow.Contains(type)
                                                                                                  select new { abnormalid, isShow, num, redirected }).ToList();
                for (var i = 1; i <= 4; i++) //5 lvl of redirection
                {
                    parser = (from item in parser join item1 in parser on item.redirected equals item1.abnormalid into wrapped
                              from wrap in wrapped.DefaultIfEmpty()
                              select new { item.abnormalid, item.isShow, item.num, redirected = wrap == null ? item.redirected : wrap.redirected }).ToList();
                    parser = parser.Distinct((x, y) => x.abnormalid == y.abnormalid && x.redirected == y.redirected, x => x.abnormalid.GetHashCode()).ToList();//don't eat all RAM in redirect loops
                }
                parser  = parser.Where(x => x.isShow != "False").ToList();
                Dotdata = (from item in xml.Root.Elements("Abnormal")
                           join item1 in parser on item.Attribute("id").Value equals item1.redirected into parsed
                           from par in parsed
                           let abnormalid = par.abnormalid
                                            let isShow = par.isShow
                                                         let isBuff = item.Attribute("isBuff") == null ? "True" : item.Attribute("isBuff").Value
                                                                      let time = item.Attribute("infinity").Value == "True" ? "0" : item.Attribute("time").Value
                                                                                 let property = item.Attribute("property") == null ? "0" : item.Attribute("property").Value
                                                                                                from eff in item.Elements("AbnormalityEffect")
                                                                                                let type = eff.Attribute("type") == null ? "0" : eff.Attribute("type").Value
                                                                                                           let method = eff.Attribute("method") == null ? "" : eff.Attribute("method").Value
                                                                                                                        let num = par.num
                                                                                                                                  let amount = eff.Attribute("value") == null ? "" : eff.Attribute("value").Value
                                                                                                                                               let tick = eff.Attribute("tickInterval") == null ? "0" : eff.Attribute("tickInterval").Value
                                                                                                                                                          where (((type == "51" || type == "52") && tick != "0") || ((isShow != "False" || abnormalid == "201" || abnormalid == "202"))) && amount != "" && method != ""
                                                                                                                                                          select new { abnormalid, type, amount, method, time, tick, num, property, isBuff, isShow }).ToList();
                Dots    = Dots.Union(Dotdata).ToList();
                parser  = parser.Distinct((x, y) => x.redirected.Equals(y.redirected), x => x.redirected.GetHashCode()).ToList();
                Missing = Missing.Union(parser).ToList();
            }
            Dots = Dots.Distinct((x, y) => x.abnormalid.Equals(y.abnormalid) && x.num == y.num, x => (x.abnormalid + "l" + x.num).GetHashCode()).ToList();
            var subs = (from dot in Dots
                        let index = "value" + (dot.num == 1 ? "" : dot.num.ToString())
                                    let change = ((dot.method == "3" || dot.method == "4" || dot.method == "0") && dot.type != "227" && dot.type != "228")
                            ? (dot.type == "51" || dot.type == "52")
                                ? Math.Abs(Math.Round(double.Parse(dot.amount, CultureInfo.InvariantCulture) * 100, 2)).ToString("r", CultureInfo.InvariantCulture) + "%"
                                : Math.Abs(Math.Round((1 - double.Parse(dot.amount, CultureInfo.InvariantCulture)) * 100, 2)).ToString("r", CultureInfo.InvariantCulture) + "%"
                            : dot.amount
                                                 select new formatter {
                abnormalid = dot.abnormalid, index = index, tick = dot.tick, change = change, time = dot.time
            }).ToDictionary(x => Tuple.Create(x.abnormalid, x.index));

            Dots = Dots.Distinct((x, y) => x.abnormalid.Equals(y.abnormalid) && x.type == y.type, x => (x.abnormalid + "l" + x.type).GetHashCode()).ToList();
            var Names = "".Select(t => new { abnormalid = string.Empty, name = string.Empty, tooltip = string.Empty }).ToList();

            foreach (
                var file in
                Directory.EnumerateFiles(RootFolder + _region + "/StrSheet_Abnormality/"))
            {
                var xml      = XDocument.Load(file);
                var Namedata = (from item in xml.Root.Elements("String")
                                let abnormalid = item.Attribute("id").Value
                                                 let name = item.Attribute("name") == null ? "" : item.Attribute("name").Value
                                                            let tooltip = item.Attribute("tooltip") == null ? "" : SubValues(item.Attribute("tooltip").Value
//                                    .Replace("$H_W_GOOD","").Replace("H_W_GOOD", "").Replace("$COLOR_END","").Replace("$H_W_BAD","").Replace("$H_W_Bad","").Replace("H_W_BAD","").Replace("$BR"," ").Replace("<br>", " ")
                                                                                                                             .Replace("\n", "$BR").Replace("\r", "$BR "), abnormalid, subs)
                                                                          where abnormalid != "" && name != "" && !name.Contains("BTS") && !tooltip.Contains("BTS")
                                                                          select new { abnormalid, name, tooltip }).ToList();
                Names = Names.Union(Namedata, (x, y) => x.abnormalid == y.abnormalid, x => x.abnormalid.GetHashCode()).ToList();
            }
            Missing.ForEach(x =>
            {
                var found = Names.FirstOrDefault(z => x.abnormalid == z.abnormalid);
                if (found != null)
                {
                    if (!Names.Any(z => z.abnormalid == x.redirected))
                    {
                        Names.Add(new { abnormalid = x.redirected, name = found.name, tooltip = found.tooltip });
                    }
                }
            });

            var Icons = "".Select(t => new { abnormalid = string.Empty, iconName = string.Empty }).ToList();

            foreach (
                var file in
                Directory.EnumerateFiles(RootFolder + _region + "/AbnormalityIconData/"))
            {
                var xml      = XDocument.Load(file);
                var IconData = (from item in xml.Root.Elements("Icon")
                                let abnormalid = item.Attribute("abnormalityId").Value
                                                 let iconName = item.Attribute("iconName") == null ? "" : item.Attribute("iconName").Value
                                                                where abnormalid != "" && iconName != ""
                                                                select new { abnormalid, iconName }).ToList();
                Icons = Icons.Union(IconData, (x, y) => x.abnormalid == y.abnormalid, x => x.abnormalid.GetHashCode()).ToList();
            }
            Missing.ForEach(x =>
            {
                var found = Icons.FirstOrDefault(z => x.abnormalid == z.abnormalid);
                if (found != null)
                {
                    if (!Icons.Any(z => z.abnormalid == x.redirected))
                    {
                        Icons.Add(new { abnormalid = x.redirected, iconName = found.iconName });
                    }
                }
            });

            var SkillToName = "".Select(t => new { skillid = string.Empty, nameid = string.Empty }).ToList();

            foreach (
                var file in
                Directory.EnumerateFiles(RootFolder + _region + "/ItemData/"))
            {
                var xml      = XDocument.Load(file);
                var itemdata = (from item in xml.Root.Elements("Item") let comb = (item.Attribute("category") == null) ? "no" : item.Attribute("category").Value let skillid = (item.Attribute("linkSkillId") == null) ? "0" : item.Attribute("linkSkillId").Value let nameid = item.Attribute("id").Value where ((comb == "combat") || (comb == "brooch") || (comb == "charm") || (comb == "magical")) && skillid != "0" && skillid != "" && nameid != "" select new { skillid, nameid });
                // filter only combat items, we don't need box openings etc.
                SkillToName.AddRange(itemdata);
            }
            var ItemNames = "".Select(t => new { nameid = string.Empty, name = string.Empty }).ToList();

            foreach (
                var file in
                Directory.EnumerateFiles(RootFolder + _region + "/StrSheet_Item/"))
            {
                var xml      = XDocument.Load(file);
                var namedata = (from item in xml.Root.Elements("String") let nameid = item.Attribute("id").Value let name = item.Attribute("string").Value where nameid != "" && name != "" && name != "[TBU]" && name != "TBU_new_in_V24" select new { nameid, name }).ToList();
                ItemNames.AddRange(namedata);
            }
            var Items = (from item in SkillToName join nam in ItemNames on item.nameid equals nam.nameid orderby item.skillid where nam.name != ""
                         select new { item.skillid, item.nameid, nam.name }).ToList();

            var ItemSkills = "".Select(t => new { skillid = string.Empty, abid = string.Empty, template = string.Empty }).ToList();

            foreach (
                var file in
                Directory.EnumerateFiles(RootFolder + _region + "/SkillData/"))
            {
                var xml = XDocument.Load(file);
                if (xml.Root.Attribute("huntingZoneId")?.Value != "0")
                {
                    continue;
                }
                var itemdata = (from skills in xml.Root.Elements("Skill")
                                let template = skills.Attribute("templateId").Value
                                               let skillid = skills.Attribute("id").Value
                                                             from abns in skills.Descendants().Where(x => x.Name == "AbnormalityOnPvp" || x.Name == "AbnormalityOnCommon")
                                                             let abid = abns.Attribute("id")?.Value ?? ""
                                                                        where template != "" &&
                                                                        skillid != "" && abid != ""
                                                                        select new { skillid, abid, template });
                ItemSkills = ItemSkills.Union(itemdata).ToList();
            }
            var ItemAbnormals = (from skills in ItemSkills
                                 join names in Items on skills.skillid equals names.skillid
                                 orderby int.Parse(names.nameid)
                                 select new { abid = skills.abid, nameid = names.nameid, names.name }).ToList();

            ItemAbnormals = ItemAbnormals.Distinct((x, y) => x.abid == y.abid, x => x.abid.GetHashCode()).ToList();

            List <Skill> skilllist;

            new SkillExtractor(_region, out skilllist);
            if (_region != "KR")
            {
                skilllist.Where(x => x.Name.Contains(":")).ToList().ForEach(x => x.Name = x.Name.Replace(":", ""));
            }
            var xml1      = XDocument.Load(RootFolder + _region + "/LobbyShape.xml");
            var templates = (from races in xml1.Root.Elements("SelectRace") let race = races.Attribute("race").Value.Cap() let gender = races.Attribute("gender").Value.Cap() from temp in races.Elements("SelectClass") let PClass = SkillExtractor.ClassConv(temp.Attribute("class").Value) let templateId = temp.Attribute("templateId").Value where temp.Attribute("available").Value == "True" select new { race, gender, PClass, templateId });

            //assume skills for different races and genders are the same per class
            templates = templates.Distinct((x, y) => x.PClass == y.PClass, x => x.PClass.GetHashCode()).ToList();
            var directSKills = (from iskills in ItemSkills
                                join temp in templates on iskills.template equals temp.templateId
                                join names in skilllist on new { temp.PClass, iskills.skillid } equals new { names.PClass, skillid = names.Id }
                                where iskills.abid != "902" //noctineum, bugged skills abnormals
                                orderby int.Parse(iskills.abid)
                                select new { abnormalid = iskills.abid, name = names.Name, iconName = names.IconName }).ToList();
            var Passives = "".Select(t => new { abnormalid = string.Empty, name = string.Empty, iconName = string.Empty }).ToList();

            foreach (var x1 in directSKills.GroupBy(x => x.abnormalid))
            {
                Passives.Add(new { abnormalid = x1.Key, name = x1.Count() > 1?SkillExtractor.RemoveLvl(x1.First().name): x1.First().name, iconName = x1.First().iconName });
            }

            xml1 = XDocument.Load(RootFolder + _region + "/StrSheet_Crest.xml");
            var xml2   = XDocument.Load(RootFolder + _region + "/CrestData.xml");
            var Glyphs = (from item in xml1.Root.Elements("String") join crestItem in xml2.Root.Elements("CrestItem") on item.Attribute("id").Value equals crestItem.Attribute("id").Value
                          let passiveid = item.Attribute("id").Value
                                          let name = item.Attribute("name").Value
                                                     let pclass = SkillExtractor.ClassConv(crestItem.Attribute("class").Value)
                                                                  let skillname = item.Attribute("skillName").Value
                                                                                  let searchname = _region == "RU"? skillname.Replace("Всплеск ярости", "Сила гиганта").Replace("Разряд бумеранга", "Возвратный разряд").Replace("Фронтальная защита", "Сзывающий клич").Replace(":", "") :
                                                                                                   _region != "KR"?skillname.Replace(":", ""): skillname
                                                                                                   let iconName1 = skilllist.Find(x => x.Name.Contains(searchname) && x.PClass == pclass)?.IconName ?? ""
                                                                                                                   let skillId1 = skilllist.Find(x => x.Name.Contains(searchname) && x.PClass == pclass)?.Id ?? ""
                                                                                                                                  let iconName = _region == "KR" || iconName1 != "" || !name.Contains(" ") ? iconName1 : skilllist.Find(x => x.Name.ToLowerInvariant().Contains(
                                                                                                                                                                                                                                            _region == "EU-FR" ? name.ToLowerInvariant().Remove(name.LastIndexOf(' ')) : name.ToLowerInvariant().Substring(name.IndexOf(' ') + 1)
                                                                                                                                                                                                                                            ))?.IconName ?? ""
                                                                                                                                                 let skillId = _region == "KR" || skillId1 != "" || !name.Contains(" ") ? skillId1 : skilllist.Find(x => x.Name.ToLowerInvariant().Contains(
                                                                                                                                                                                                                                                        _region == "EU-FR" ? name.ToLowerInvariant().Remove(name.LastIndexOf(' ')) : name.ToLowerInvariant().Substring(name.IndexOf(' ') + 1)
                                                                                                                                                                                                                                                        ))?.Id ?? ""
                                                                                                                                                               let tooltip = item.Attribute("tooltip").Value
                                                                                                                                                                             select new { passiveid, name, skillname, skillId, iconName, tooltip });

            //dont parse CrestData.xml for passiveid<=>crestid, since they are identical now
            foreach (
                var file in
                Directory.EnumerateFiles(RootFolder + _region + "/Passivity/"))
            {
                var xml         = XDocument.Load(file);
                var PassiveData = (from item in xml.Root.Elements("Passive")
                                   let abnormalid = item.Attribute("value").Value
                                                    let passiveid = item.Attribute("id").Value
                                                                    let type = item.Attribute("type").Value
                                                                               join glyph in Glyphs on passiveid equals glyph.passiveid
                                                                               where abnormalid != "" && (type == "209" || type == "210" || type == "156" || type == "157" || type == "80" || type == "232" || type == "106") &&
                                                                               abnormalid != "500100"
                                                                               select new { abnormalid, name = (isGlyph(glyph.name))?$"{glyph.skillname}({glyph.name})": glyph.name, glyph.iconName }).ToList();
                Passives = Passives.Union(PassiveData, (x, y) => (x.abnormalid == y.abnormalid), x => x.abnormalid.GetHashCode()).ToList();
            }

            Dotlist = (from dot in Dots
                       join nam in Names on dot.abnormalid equals nam.abnormalid into inames
                       join icon in Icons on dot.abnormalid equals icon.abnormalid into iicons
                       join skills in ItemAbnormals on dot.abnormalid equals skills.abid into iskills
                       join glyph in Passives on dot.abnormalid equals glyph.abnormalid into gskills
                       from iname in inames.DefaultIfEmpty()
                       from iicon in iicons.DefaultIfEmpty()
                       from iskill in iskills.DefaultIfEmpty()
                       from gskill in gskills.DefaultIfEmpty()

                       where (iname != null || iskill != null || gskill != null)
                       orderby int.Parse(dot.abnormalid), int.Parse(dot.type)
                       select new HotDot(int.Parse(dot.abnormalid), dot.type, double.Parse(dot.amount, CultureInfo.InvariantCulture), dot.method, long.Parse(dot.time), int.Parse(dot.tick), gskill == null?iname?.name ?? iskill.name:gskill.name, iskill == null?"":iskill.nameid, iskill == null ? "" : iskill.name, iname?.tooltip ?? "", gskill == null?iicon?.iconName ?? "":gskill.iconName, dot.property, bool.Parse(dot.isBuff), dot.isShow != "False", iicon?.iconName ?? "")).ToList();

            var Crests = "".Select(t => new { passiveid = string.Empty, skillname = string.Empty, skillId = string.Empty, iconName = string.Empty, name = string.Empty, glyphIcon = string.Empty, tooltip = string.Empty }).ToList();

            xml1 = XDocument.Load(RootFolder + _region + "/CrestIconData.xml");
            foreach (
                var file in
                Directory.EnumerateFiles(RootFolder + _region + "/Passivity/"))
            {
                var xml        = XDocument.Load(file);
                var CrestsData = (from item in xml.Root.Elements("Passive")
                                  let value = double.Parse(item.Attribute("value")?.Value ?? "0", CultureInfo.InvariantCulture)
                                              let prob = double.Parse(item.Attribute("prob").Value ?? "0", CultureInfo.InvariantCulture) * 100
                                                         let type = item.Attribute("type").Value
                                                                    let passiveid = item.Attribute("id").Value
                                                                                    join glyph in Glyphs on passiveid equals glyph.passiveid
                                                                                    join icon in xml1.Root.Elements("Icon") on passiveid equals icon.Attribute("crestId").Value
                                                                                    let glyphIcon = icon.Attribute("iconName").Value
                                                                                                    let tooltip = glyph.tooltip == null ? "" : glyph.tooltip.Replace("$BR", " ").Replace("\n", " ").Replace("$value",
                                                                                                                                                                                                            type == "72"?(-value).ToString():type == "171" || type == "26" || type == "175"? value.ToString():Math.Round(Math.Abs(value * 100 - 100), 2) + "%"
                                                                                                                                                                                                            ).Replace("$prob", prob + "%")
                                                                                                                  select new { passiveid, glyph.skillname, glyph.skillId, glyph.iconName, glyph.name, glyphIcon, tooltip }).ToList();
                Crests = Crests.Union(CrestsData, (x, y) => (x.passiveid == y.passiveid), x => x.passiveid.GetHashCode()).ToList();
            }

            var outputFile = new StreamWriter(Path.Combine(OutFolder, $"glyph-{_region}.tsv"));

            foreach (var glyph in Crests)
            {
                outputFile.WriteLine(glyph.passiveid + "\t" + glyph.skillname + "\t" + glyph.skillId + "\t" + glyph.iconName.ToLowerInvariant() + "\t" + glyph.name + "\t" + glyph.glyphIcon.ToLowerInvariant() + "\t" + glyph.tooltip);
                Program.Copytexture(glyph.glyphIcon);
                Program.Copytexture(glyph.iconName);
            }
            outputFile.Flush();
            outputFile.Close();
        }
示例#2
0
        private void Monsters()
        {
            var xml      = XDocument.Load(RootFolder + _region + "/ContinentData/ContinentData-0.xml");
            var contdata = (from cont in xml.Root.Elements("Continent") let battle = (cont.Attribute("channelType") == null)?false:cont.Attribute("channelType").Value == "battleField" let idcont = cont.Attribute("id").Value from hunting in cont.Elements("HuntingZone") let idzone = hunting.Attribute("id").Value where idcont != "" && idzone != "" select new { idcont, idzone, battle }).ToList();

            xml = XDocument.Load(RootFolder + _region + "/LobbyShape/LobbyShape-0.xml");
            var templates = (from races in xml.Root.Elements("SelectRace") let race = races.Attribute("race").Value.Cap() let gender = races.Attribute("gender").Value.Cap() from temp in races.Elements("SelectClass") let PClass = SkillExtractor.ClassConv(temp.Attribute("class").Value) let templateId = temp.Attribute("templateId").Value where temp.Attribute("available").Value == "True" select new { race, gender, PClass, templateId });

            //assume skills for different races and genders are the same per class
            templates = templates.Distinct((x, y) => x.PClass == y.PClass, x => x.PClass.GetHashCode()).ToList();
            var summons = "".Select(t => new { id = string.Empty, skillId = string.Empty, PClass = string.Empty }).ToList();

            foreach (
                var file in
                Directory.EnumerateFiles(RootFolder + _region + "/SkillData/"))
            {
                xml = XDocument.Load(file);
                if (xml.Root.Attribute("huntingZoneId")?.Value != "0")
                {
                    continue;
                }
                var summonData = (from temp in templates join skills in xml.Root.Elements("Skill") on temp.templateId equals skills.Attribute("templateId").Value into Pskills
                                  from Pskill in Pskills
                                  let PClass = temp.PClass
                                               let skillId = Pskill.Attribute("id").Value
                                                             let id = Pskill.Descendants("SummonNpc").FirstOrDefault()?.Attribute("templateId")?.Value ?? ""
                                                                      where skillId != "" && PClass != "" && id != "" select new { id, skillId, PClass });
                summons = summons.Union(summonData, (x, y) => x.PClass == y.PClass && x.id == y.id, x => (x.PClass + x.id).GetHashCode()).ToList();
            }

            List <Skill> skilllist;

            new SkillExtractor(_region, out skilllist);

            var summonNames = (from item in skilllist join summon in summons on new { skillId = item.Id, PClass = item.PClass } equals new { summon.skillId, summon.PClass } into results
                               from res in results
                               let name = item.Name
                                          where res.id != "" && name != ""
                                          select new { idzone = "1023", identity = res.id, name }).ToList();

            xml = XDocument.Load(RootFolder + _region + "/StrSheet_Region/StrSheet_Region-0.xml");
            var regdata = (from str in xml.Root.Elements("String") let idname = str.Attribute("id").Value let regname = str.Attribute("string").Value where idname != "" && regname != "" select new { idname, regname }).ToList();

            var contToStr = "".Select(t => new { idcont = string.Empty, nameid = string.Empty }).ToList();

            foreach (
                var file in
                Directory.EnumerateFiles(RootFolder + _region + "/Area/"))
            {
                xml = XDocument.Load(file);
                var areadata = (from area in xml.Root.Document.Elements("Area") let idcont = area.Attribute("continentId").Value let nameid = area.Attribute("nameId").Value where idcont != "" && nameid != "" select new { idcont, nameid });
                contToStr = contToStr.Union(areadata).ToList();
            }

            xml = XDocument.Load(RootFolder + _region + "/StrSheet_Dungeon/StrSheet_Dungeon-0.xml");
            var dundata = (from dun in xml.Root.Elements("String") let idcont = dun.Attribute("id").Value let dunname = dun.Attribute("string").Value where idcont != "" && dunname != "" select new { idcont, dunname }).ToList();

            var regdd = (from contn in contToStr
                         join reg in regdata on contn.nameid equals reg.idname into regn
                         from rg in regn
                         join dun in dundata on contn.idcont equals dun.idcont into regdun
                         from rd in regdun.DefaultIfEmpty()
                         select new { idcont = contn.idcont, regname = rd == null ? rg.regname : rd.dunname, nameid = contn.nameid }).ToList();

            var zonedata = (from rd in regdd
                            from cont in contdata
                            where rd.idcont == cont.idcont
                            let idzone = Convert.ToInt32(cont.idzone) let battle = cont.battle
                                                                                   let prio = (!rd.nameid.StartsWith(cont.idzone))
                                                                                              orderby idzone, prio
                            select new { Id = idzone, Name = rd.regname, battle }).ToList();

            zonedata = zonedata.Distinct((x, y) => x.Id == y.Id, (x) => x.Id.GetHashCode()).ToList();
            //using (StreamWriter outputFile = new StreamWriter("data/cont.csv"))
            //{
            //    foreach (var line in zonedata)
            //    {
            //        outputFile.WriteLine("{0};{1}", line.Id, line.Name);
            //    }
            //}
            xml = XDocument.Load(RootFolder + _region + "/StrSheet_Creature/StrSheet_Creature-0.xml");
            var mobdata = (from hunting in xml.Root.Elements("HuntingZone")
                           let idzone = hunting.Attribute("id").Value
                                        from entity in hunting.Elements("String") join summon in summonNames on new { idzone, identity = entity.Attribute("templateId").Value } equals new { summon.idzone, summon.identity } into results
                           from res in results.DefaultIfEmpty()
                           let identity = entity.Attribute("templateId").Value
                                          let name = string.IsNullOrWhiteSpace(res?.name)?entity.Attribute("name").Value : res.name
                                                     where name != "" && identity != "" && idzone != "" select new { idzone, identity, name }).ToList();

            mobdata = mobdata.Union(summonNames).ToList();

            var mobprop = "".Select(t => new { idzone = string.Empty, id = string.Empty, boss = false, maxHP = string.Empty, size = string.Empty }).ToList();

            foreach (
                var file in
                Directory.EnumerateFiles(RootFolder + _region + "/NpcData/"))
            {
                xml = XDocument.Load(file);
                var mobpdata = (from hunting in xml.Root.Document.Elements("NpcData") let idzone = hunting.Attribute("huntingZoneId").Value
                                                                                                   from entity in hunting.Elements("Template") let id = entity.Attribute("id").Value
                                                                                                                                                        let boss = (entity.Attribute("showAggroTarget") == null)?false: bool.Parse(entity.Attribute("showAggroTarget").Value)
                                                                                                                                                                   let size = (entity.Attribute("size") == null) ? "" : entity.Attribute("size").Value
                                                                                                                                                                              from stat in entity.Elements("Stat") let maxHP = stat.Attribute("maxHp").Value
                                                                                                                                                                                                                               where id != "" && idzone != "" select new { idzone, id, boss, maxHP, size }).ToList();
                mobprop = mobprop.Union(mobpdata).ToList();
            }
            var moball = (from mobd in mobdata join mobb in mobprop on new { mobd.idzone, id = mobd.identity } equals new { mobb.idzone, mobb.id } into moba
                          from mobs in moba.DefaultIfEmpty()
                          let idzone = Convert.ToInt32(mobd.idzone) let identity = Convert.ToInt32(mobd.identity)
                                                                                   orderby idzone, identity select new { idzone, identity, mobd.name, boss = mobs == null?false:mobs.boss, maxHP = mobs == null?"0":mobs.maxHP, size = mobs == null ? "" : mobs.size }).ToList();
            var alldata = (from mobs in moball join zoned in zonedata on mobs.idzone equals zoned.Id into zones
                           from zone in zones.DefaultIfEmpty()
                           orderby mobs.idzone, mobs.identity select new { mobs.idzone, regname = zone == null?"unknown":zone.Name, mobs.identity, mobs.name, boss = (zone == null)?false:zone.battle?false:mobs.boss, mobs.maxHP, mobs.size }).ToList();

            var bossOverride = new Dictionary <int, Dictionary <int, bool> >();
            var nameOverride = new Dictionary <int, Dictionary <int, string> >();

            if (File.Exists(RootFolder + "override/monsters-override.xml"))
            {
                var isBossOverrideXml = XDocument.Load(RootFolder + "override/monsters-override.xml");
                foreach (var zone in isBossOverrideXml.Root.Elements("Zone"))
                {
                    var id = int.Parse(zone.Attribute("id").Value);
                    bossOverride.Add(id, new Dictionary <int, bool>());
                    nameOverride.Add(id, new Dictionary <int, string>());
                    foreach (var monster in zone.Elements("Monster"))
                    {
                        var monsterId = int.Parse(monster.Attribute("id").Value);
                        var isBoss    = monster.Attribute("isBoss");
                        if (isBoss != null)
                        {
                            var isBossString = isBoss.Value.ToLower();
                            bossOverride[id].Add(monsterId, isBossString == "true");
                        }
                        var bossName = monster.Attribute("name");
                        if (bossName == null)
                        {
                            continue;
                        }
                        var nameOverrideString = bossName.Value;
                        nameOverride[id].Add(monsterId, nameOverrideString);
                    }
                }
            }
            foreach (var all in alldata)
            {
                if (!_zones.ContainsKey(all.idzone))
                {
                    _zones.Add(all.idzone, new Zone(all.idzone, all.regname));
                }
                bool isboss = all.boss;
                if (bossOverride.ContainsKey(all.idzone) && bossOverride[all.idzone].ContainsKey(all.identity))
                {
                    isboss = bossOverride[all.idzone][all.identity];
                }
                string name = all.name;
                if (nameOverride.ContainsKey(all.idzone) && nameOverride[all.idzone].ContainsKey(all.identity))
                {
                    name = nameOverride[all.idzone][all.identity];
                }
                if (name.Contains("{@Creature:"))
                {
                    int zone  = int.Parse(name.Substring(name.IndexOf("{@Creature:") + 11, name.IndexOf("#") - name.IndexOf("{@Creature:") - 11));
                    int id    = int.Parse(name.Substring(name.IndexOf("#") + 1, name.IndexOf("}") - name.IndexOf("#") - 1));
                    var subst = alldata.First(x => (x.idzone == zone && x.identity == id)).name;
                    name = name.Replace(name.Substring(name.IndexOf("{"), name.IndexOf("}") - name.IndexOf("{") + 1), subst);
                }
                if (name.Contains("{@Rgn:"))
                {
                    name = name.Replace("{@Rgn:", "").Replace("}", "");
                }

                if (!_zones[all.idzone].Monsters.ContainsKey(all.identity))
                {
                    _zones[all.idzone].Monsters.Add(all.identity, new Monster(all.identity, name, all.maxHP, isboss));
                }
            }
        }
        //private void AddCharms()
        //{
        //    var xml = XDocument.Load(RootFolder + _region + "/StrSheet_Charm.xml");
        //    var xml1 = XDocument.Load(RootFolder + _region + "/CharmIconData.xml");
        //    var charmList = (from item in xml.Root.Elements("String")
        //                     join icon in xml1.Root.Elements("Icon") on item.Attribute("id").Value equals icon.Attribute("charmId").Value
        //                     let id = item.Attribute("id").Value
        //                     let name = item.Attribute("string").Value
        //                     let tooltip = item.Attribute("tooltip").Value
        //                     let iconName = icon.Attribute("iconName").Value
        //                     select new HotDot(int.Parse(id), "Charm", 0, "0", 0, 0, name, "", "",tooltip, iconName, "Buff", true, true, iconName)).ToList();
        //    Dotlist = Dotlist.Union(charmList).ToList();
        //}
        private void RawExtract(DataCenter dc, List <Skill> skilllist, List <Template> templates)
        {
            var abnormalityDC = dc.Root.Children("Abnormality").SelectMany(x => x.Children("Abnormal"));
            //var interesting = new string[] {"1", "3", "4", "5", "6", "7", "8", "9", "18", "19","36", "20", "22", "24", "25", "27", "28", "30", "103", "104", "105", "108", "162", "167", "168", "203", "207", "210", "208", "221", "227", "229", "231", "235", "236", "237" , "249", "255","260", "283", "316" };
            //var notinteresting = new string[] {"8", "9", "18", "20", "27", "28", "103", "105", "108", "168", "221", "227" };
            var redirects_to_ignore = new int[] { 161, 182, 252, 264 };
            var redirects_to_follow = new int[] { 64, 161, 182, 223, 248, 252, 264, 271 };
            var Dots = (from item in abnormalityDC
                        let abnormalid = item["id", 0].ToInt32()
                                         let isShow = item["isShow", "False"].AsString != "False"
                                                      let property = item["property", 0].ToString()
                                                                     let isBuff = item["isBuff", true].ToBoolean()
                                                                                  let time = item["infinity", false].ToBoolean() ? "0" : item["time", "0"].AsString
                                                                                             from eff in item.Children("AbnormalityEffect").DefaultIfEmpty()
                                                                                             let type = eff?["type", 0].ToInt32() ?? 0
                                                                                                        let method = eff?["method", 0].ToInt32() ?? 0
                                                                                                                     let num = item.Children("AbnormalityEffect").TakeWhile(x => x != eff).Count() + 1
                                                                                                                               let amount = eff?["value", "0"].AsString ?? "0"
                                                                                                                                            let tick = eff?["tickInterval", 0].ToString() ?? "0"
                                                                                                                                                       where (((type == 51 || type == 52) && tick != "0") || (!redirects_to_ignore.Contains(type)))                                                                   // && amount != "" && method != -1
//                    where (((type == "51" || type == "52") && tick != "0") || (!redirects_to_ignore.Contains(type) && (isShow != "False" || abnormalid == "201" || abnormalid == "202" || abnormalid == "10152050"))) && amount != "" && method != ""
                                                                                                                                                       select new { abnormalid, type, amount = amount.Contains(',')?amount.Split(',').Last() : amount, method, time, tick, num, property, isBuff, isShow }).ToList(); //// 201 202 - marked as not shown, but needed
            var parser = (from item in abnormalityDC
                          let abnormalid = item["id", 0].ToInt32()
                                           let isShow = item["isShow", "False"].AsString != "False"
                                                        from eff in item.Children("AbnormalityEffect")
                                                        let type = eff["type", 0].ToInt32()
                                                                   where redirects_to_follow.Contains(type)
                                                                   let num = item.Children("AbnormalityEffect").TakeWhile(x => x != eff).Count() + 1
                                                                             let redirected = eff["value", 0].ToInt32()
                                                                                              select new { abnormalid, isShow, num, redirected }).ToList();

            for (var i = 1; i <= 4; i++) //5 lvl of redirection
            {
                parser = (from item in parser join item1 in parser on item.redirected equals item1.abnormalid into wrapped
                          from wrap in wrapped.DefaultIfEmpty()
                          select new { item.abnormalid, item.isShow, item.num, redirected = wrap == null ? item.redirected : wrap.redirected }).ToList();
                parser = parser.Distinct((x, y) => x.abnormalid == y.abnormalid && x.redirected == y.redirected, x => x.abnormalid.GetHashCode()).ToList();    //don't eat all RAM in redirect loops
            }
            parser = parser.Where(x => x.isShow).ToList();
            var Dotdata = (from item in abnormalityDC
                           join item1 in parser on item["id", 0].ToInt32() equals item1.redirected into parsed
                           from par in parsed
                           let abnormalid = par.abnormalid
                                            let isShow = par.isShow
                                                         let isBuff = item["isBuff", true].ToBoolean()
                                                                      let time = item["infinity", false].ToBoolean() ? "0" : item["time", "0"].AsString
                                                                                 let property = item["property", 0].ToString()
                                                                                                from eff in item.Children("AbnormalityEffect")
                                                                                                let type = eff["type", -1].ToInt32()
                                                                                                           let method = eff["method", -1].ToInt32()
                                                                                                                        let num = par.num
                                                                                                                                  let amount = eff["value", ""].AsString
                                                                                                                                               let tick = eff["tickInterval", 0].ToString()
                                                                                                                                                          where (((type == 51 || type == 52) && tick != "0") || ((isShow || abnormalid == 201 || abnormalid == 202))) && amount != "" && method != -1
                                                                                                                                                          select new { abnormalid, type, amount, method, time, tick, num, property, isBuff, isShow }).ToList();

            Dots = Dots.Union(Dotdata).ToList();
            var Missing = parser.Distinct((x, y) => x.redirected.Equals(y.redirected), x => x.redirected.GetHashCode()).ToList();

            Dots = Dots.Distinct((x, y) => x.abnormalid.Equals(y.abnormalid) && x.num == y.num, x => (x.abnormalid, x.num).GetHashCode()).ToList();
            var subs = (from dot in Dots
                        let change = ((dot.method == 3 || dot.method == 4 || dot.method == 0) && dot.type != 227 && dot.type != 228)
                            ? (dot.type == 51 || dot.type == 52)
                                ? Math.Abs(Math.Round(double.Parse(dot.amount, CultureInfo.InvariantCulture) * 100, 2)).ToString("r", CultureInfo.InvariantCulture) + "%"
                                : Math.Abs(Math.Round((1 - double.Parse(dot.amount, CultureInfo.InvariantCulture)) * 100, 2)).ToString("r", CultureInfo.InvariantCulture) + "%"
                            : dot.amount
                                     select new formatter {
                abnormalid = dot.abnormalid, index = dot.num, tick = dot.tick, change = change, time = dot.time
            })
                       .GroupBy(x => x.abnormalid).ToDictionary(g => g.Key, g => g.ToDictionary(h => h.index));

            Dots = Dots.Distinct((x, y) => x.abnormalid.Equals(y.abnormalid) && x.type == y.type, x => (x.abnormalid, x.type).GetHashCode()).ToList();

            var Names = (from item in dc.Root.Children("StrSheet_Abnormality").SelectMany(x => x.Children("String"))
                         let abnormalid = item["id", 0].ToInt32()
                                          let name = item["name", ""].AsString
                                                     where abnormalid != 0 && name != "" && !name.Contains("BTS")
                                                     let tooltip = item["tooltip"].IsNull ? "" : SubValues(item["tooltip", ""].AsString
                                                                                                           //                                    .Replace("$H_W_GOOD","").Replace("H_W_GOOD", "").Replace("$COLOR_END","").Replace("$H_W_BAD","").Replace("$H_W_Bad","").Replace("H_W_BAD","").Replace("$BR"," ").Replace("<br>", " ")
                                                                                                           .Replace("\n", "$BR").Replace("\r", "$BR "), abnormalid, subs.GetValueOrDefault(abnormalid))
                                                                   where !tooltip.Contains("BTS")
                                                                   select new { abnormalid, name, tooltip }).Distinct((x, y) => x.abnormalid == y.abnormalid, x => x.abnormalid.GetHashCode()).ToList();

            Missing.ForEach(x =>
            {
                var found = Names.FirstOrDefault(z => x.abnormalid == z.abnormalid);
                if (found != null)
                {
                    if (!Names.Any(z => z.abnormalid == x.redirected))
                    {
                        Names.Add(new { abnormalid = x.redirected, name = found.name, tooltip = found.tooltip });
                    }
                }
            });

            var Icons = (from item in dc.Root.Children("AbnormalityIconData").SelectMany(x => x.Children("Icon"))
                         let abnormalid = item["abnormalityId", 0].ToInt32()
                                          let iconName = item["iconName", ""].AsString
                                                         where abnormalid != 0 && iconName != ""
                                                         select new { abnormalid, iconName }).Distinct((x, y) => x.abnormalid == y.abnormalid, x => x.abnormalid.GetHashCode()).ToList();

            Missing.ForEach(x =>
            {
                var found = Icons.FirstOrDefault(z => x.abnormalid == z.abnormalid);
                if (found != null)
                {
                    if (!Icons.Any(z => z.abnormalid == x.redirected))
                    {
                        Icons.Add(new { abnormalid = x.redirected, iconName = found.iconName });
                    }
                }
            });

            var SkillToName = (from item in dc.Root.Children("ItemData").SelectMany(x => x.Children("Item"))
                               let comb = item["category", "no"].AsString
                                          let skillid = item["linkSkillId", 0].ToInt32()
                                                        let nameid = item["id", 0].ToInt32()
                                                                     where ((comb == "combat") || (comb == "brooch") || (comb == "charm") || (comb == "magical")) && skillid != 0 && nameid != 0 select new { skillid, nameid });
            // filter only combat items, we don't need box openings etc.
            var ItemNames = (from item in dc.Root.Children("StrSheet_Item").SelectMany(x => x.Children("String"))
                             let nameid = item["id", 0].ToInt32()
                                          let name = item["string", ""].AsString
                                                     where nameid != 0 && name != "" && name != "[TBU]" && name != "TBU_new_in_V24" select new { nameid, name }).ToList();
            var Items = (from item in SkillToName join nam in ItemNames on item.nameid equals nam.nameid orderby item.skillid where nam.name != ""
                         select new { item.skillid, item.nameid, nam.name }).ToList();

            string[] abnormalFilter = { "AbnormalityOnPvp", "AbnormalityOnCommon" };
            var      ItemSkills     = (from skill in dc.Root.Children("SkillData").SelectMany(x => x.Children("Skill"))
                                       let template = skill["templateId", 0].ToInt32()
                                                      let skillid = skill["id", 0].ToInt32()
                                                                    where skill.Parent["huntingZoneId", 0].ToInt32() == 0 && template != 0 && skillid != 0
                                                                    from TargetingList in skill.Children("TargetingList")
                                                                    from Targeting in TargetingList.Children("Targeting")
                                                                    from AreaList in Targeting.Children("AreaList")
                                                                    from Area in AreaList.Children("Area")
                                                                    from Effect in Area.Children("Effect")
                                                                    from Abnormal in Effect.Children(abnormalFilter)
                                                                    let abid = Abnormal["id", 0].ToInt32()
                                                                               where abid != 0
                                                                               select new { skillid, abid, template }).ToList();
            var ItemAbnormals = (from skills in ItemSkills
                                 join names in Items on skills.skillid equals names.skillid
                                 orderby names.nameid
                                 select new { abid = skills.abid, nameid = names.nameid, names.name }).ToList();

            ItemAbnormals = ItemAbnormals.Distinct((x, y) => x.abid == y.abid, x => x.abid.GetHashCode()).ToList();

            if (_region != "KR")
            {
                skilllist.Where(x => x.Name.Contains(":")).ToList().ForEach(x => x.Name = x.Name.Replace(":", ""));
            }
            var directSKills = (from iskills in ItemSkills
                                join temp in templates on iskills.template equals temp.templateId
                                join names in skilllist on new { temp.PClass, iskills.skillid } equals new { names.PClass, skillid = names.Id }
                                where iskills.abid != 902 //noctineum, bugged skills abnormals
                                orderby iskills.abid
                                select new { abnormalid = iskills.abid, name = names.Name, iconName = names.IconName }).ToList();

            var Passives = directSKills.GroupBy(x => x.abnormalid).Where(x => x.Key != 400500 && x.Key != 400501 && x.Key != 400508).Select(x1 => new {
                abnormalid = x1.Key,
                name       = x1.Count() > 1 ? SkillExtractor.RemoveLvl(x1.First().name) : x1.First().name,
                iconName   = x1.First().iconName
            }).ToList();

            var Glyphs = (from item in dc.Root.FirstChild("StrSheet_Crest").Children("String")
                          join crestItem in dc.Root.FirstChild("CrestData").Children("CrestItem") on item["id", 0].ToInt32() equals crestItem["id", 0].ToInt32()
                          let passiveid = item["id", 0].ToInt32()
                                          let name = item["name", ""].AsString
                                                     let pclass = SkillExtractor.ClassConv(crestItem["class", ""].AsString)
                                                                  let skillname = item["skillName", ""].AsString
                                                                                  let searchname = _region == "RU"? skillname.Replace("Всплеск ярости", "Сила гиганта").Replace("Разряд бумеранга", "Возвратный разряд").Replace("Фронтальная защита", "Сзывающий клич").Replace(":", "") :
                                                                                                   _region != "KR"?skillname.Replace(":", ""): skillname
                                                                                                   let iconName1 = skilllist.Find(x => x.Name.Contains(searchname) && x.PClass == pclass)?.IconName ?? ""
                                                                                                                   let skillId1 = skilllist.Find(x => x.Name.Contains(searchname) && x.PClass == pclass)?.Id ?? 0
                                                                                                                                  let iconName = _region == "KR" || iconName1 != "" || !name.Contains(" ") ? iconName1 : skilllist.Find(x => x.Name.ToLowerInvariant().Contains(
                                                                                                                                                                                                                                            _region == "EU-FR" ? name.ToLowerInvariant().Remove(name.LastIndexOf(' ')) : name.ToLowerInvariant().Substring(name.IndexOf(' ') + 1)
                                                                                                                                                                                                                                            ))?.IconName ?? ""
                                                                                                                                                 let skillId = _region == "KR" || skillId1 != 0 || !name.Contains(" ") ? skillId1 : skilllist.Find(x => x.Name.ToLowerInvariant().Contains(
                                                                                                                                                                                                                                                       _region == "EU-FR" ? name.ToLowerInvariant().Remove(name.LastIndexOf(' ')) : name.ToLowerInvariant().Substring(name.IndexOf(' ') + 1)
                                                                                                                                                                                                                                                       ))?.Id ?? 0
                                                                                                                                                               let tooltip = item["tooltip", ""].AsString
                                                                                                                                                                             select new { passiveid, name, skillname, skillId, iconName, tooltip });
            //dont parse CrestData.xml for passiveid<=>crestid, since they are identical now
            var PassiveData = (from item in dc.Root.Children("Passivity").SelectMany(x => x.Children("Passive"))
                               let passiveid = item["id", 0].ToInt32()
                                               join glyph in Glyphs on passiveid equals glyph.passiveid
                                               let type = item["type", 0].ToInt32()
                                                          let method = item["method", 0].ToInt32()
                                                                       where (type == 209 || (type == 210 && method == 4) || type == 156 || type == 157 || type == 80 || type == 232 || type == 106)
                                                                       let abnormalid = item["value", 0].ToInt32()
                                                                                        where abnormalid != 0 && abnormalid != 500100
                                                                                        select new { abnormalid, name = (isGlyph(glyph.name))?$"{glyph.skillname}({glyph.name})": glyph.name, glyph.iconName }).ToList();

            Passives = Passives.Union(PassiveData, (x, y) => (x.abnormalid == y.abnormalid), x => x.abnormalid.GetHashCode()).ToList();

            Dotlist = (from dot in Dots
                       join nam in Names on dot.abnormalid equals nam.abnormalid into inames
                       join icon in Icons on dot.abnormalid equals icon.abnormalid into iicons
                       join skills in ItemAbnormals on dot.abnormalid equals skills.abid into iskills
                       join glyph in Passives on dot.abnormalid equals glyph.abnormalid into gskills
                       from iname in inames.DefaultIfEmpty()
                       from iicon in iicons.DefaultIfEmpty()
                       from iskill in iskills.DefaultIfEmpty()
                       from gskill in gskills.DefaultIfEmpty()

                       where (iname != null || iskill != null || gskill != null)
                       orderby dot.abnormalid, dot.type
                       select new HotDot(dot.abnormalid, dot.type, double.Parse(dot.amount, CultureInfo.InvariantCulture), dot.method, long.Parse(dot.time), (int)Math.Floor(double.Parse(dot.tick, CultureInfo.InvariantCulture)), gskill == null?iname?.name ?? iskill.name:gskill.name, iskill == null?"":iskill.nameid.ToString(), iskill == null ? "" : iskill.name, iname?.tooltip ?? "", gskill == null?iicon?.iconName ?? "":gskill.iconName, dot.property, dot.isBuff, dot.isShow, iicon?.iconName ?? "")).ToList();

            var Crests = (from item in dc.Root.Children("Passivity").SelectMany(x => x.Children("Passive"))
                          let value = item["value", 0f].ToSingle()
                                      let prob = item["prob", 0f].ToSingle() * 100
                                                 let type = item["type", 0].ToInt32()
                                                            let passiveid = item["id", 0].ToInt32()
                                                                            join glyph in Glyphs on passiveid equals glyph.passiveid
                                                                            join icon in dc.Root.FirstChild("CrestIconData").Children("Icon") on passiveid equals icon["crestId", 0].ToInt32()
                                                                            let glyphIcon = icon["iconName", ""].AsString
                                                                                            let tooltip = glyph.tooltip == null ? "" : glyph.tooltip.Replace("$BR", " ").Replace("\n", " ").Replace("$value",
                                                                                                                                                                                                    type == 72?(-value).ToString():type == 171 || type == 26 || type == 175 ? value.ToString():Math.Round(Math.Abs(value * 100 - 100), 2) + "%"
                                                                                                                                                                                                    ).Replace("$prob", prob + "%")
                                                                                                          select new { passiveid, glyph.skillname, glyph.skillId, glyph.iconName, glyph.name, glyphIcon, tooltip })
                         .Distinct((x, y) => (x.passiveid == y.passiveid), x => x.passiveid.GetHashCode()).ToList();

            var outputFile = new StreamWriter(Path.Combine(OutFolder, $"glyph-{_region}.tsv"));

            foreach (var glyph in Crests)
            {
                outputFile.WriteLine(glyph.passiveid + "\t" + glyph.skillname + "\t" + glyph.skillId + "\t" + glyph.iconName.ToLowerInvariant() + "\t" + glyph.name + "\t" + glyph.glyphIcon.ToLowerInvariant() + "\t" + glyph.tooltip);
                Program.Copytexture(glyph.glyphIcon);
                Program.Copytexture(glyph.iconName);
            }
            outputFile.Flush();
            outputFile.Close();
        }