/// <summary> /// Reads each line in each section, and returns a ConverterDeck which is populated with all cards and deck name. /// </summary> /// <param name="sectionLines">A collection of deck sections with Item1 as Name, and Item2 as a collection of lines of text which are cards</param> /// <param name="deckSectionNames">List of the name of each section for the deck being converted.</param> /// <returns>A ConverterDeck object populated with all cards and deck name</returns> internal static ConverterDeck ConvertDeckWithSeparateSections(Dictionary <string, IEnumerable <string> > sectionLines, IEnumerable <string> deckSectionNames) { ConverterDeck converterDeck = new ConverterDeck(deckSectionNames); foreach (KeyValuePair <string, IEnumerable <string> > section in sectionLines) { ConverterSection converterSection = converterDeck.ConverterSections.First(cs => cs.SectionName.Equals(section.Key, StringComparison.InvariantCultureIgnoreCase)); foreach (string line in section.Value) { // The line is the Deck Name? Record it. string potentialDeckName = TextConverter.RegexMatch_DeckName(line); if (potentialDeckName != null) { converterDeck.DeckName = potentialDeckName; } else { ConverterMapping potentialCard = TextConverter.ParseLineForCardAndQuantity(line); if (potentialCard != null) { converterSection.AddConverterMapping(potentialCard); } else { converterSection.AddIncorrectlyFormattedLine(line); } } } } converterDeck.ConversionSuccessful = true; return(converterDeck); }
/// <summary> /// Adds a Mage Stats card to accompany the Mage card, since it is required in OCTGN /// </summary> private static void AddMageStatsCard(ConverterDeck converterDeck) { ConverterSection mageSection = converterDeck.ConverterSections.First(s => s.SectionName.Equals("Mage", StringComparison.InvariantCultureIgnoreCase)); if (mageSection.SectionMappings.Any(sm => sm.CardName.EndsWith(" Stats"))) { // The corresponding Stats card already appears to be added return; } ConverterMapping firstMage = mageSection.SectionMappings.FirstOrDefault(); if (firstMage != null) { string mageName = firstMage.CardName; if (MW.RegexMatch_WizardSubtype(mageName)) { // No matter what the subtype of Wizard is, it should have a 'Wizard Stats' card mageName = "Wizard"; } mageSection.AddConverterMapping(new ConverterMapping(mageName + " Stats", string.Empty, 1)); } }
private static void VerifyConverterSectionsAreIdentical(ConverterSection converterSection, ConverterSection otherConverterSection) { Assert.AreEqual(converterSection.SectionCount, otherConverterSection.SectionCount); Assert.AreEqual(converterSection.SectionMappings.Count, otherConverterSection.SectionMappings.Count); foreach (ConverterMapping converterMapping in converterSection.SectionMappings) { ConverterMapping otherConverterMapping = otherConverterSection.SectionMappings.First(sm => sm.CardName == converterMapping.CardName); VerifyConverterMappingsAreIdentical(converterMapping, otherConverterMapping); } }
/// <summary> /// Reads the lines which has Sideboard cards denoted on each line, and returns a ConverterDeck which is populated with all cards and deck name. /// </summary> /// <param name="fullPathName">The full path name of the Deck file to convert</param> /// <param name="deckSectionNames">List of the name of each section for the deck being converted.</param> /// <returns>A ConverterDeck object populated with all cards and deck name</returns> public static ConverterDeck ConvertMTGDeckWithSideBoardCardsListedEachLine(IEnumerable <string> lines, IEnumerable <string> deckSectionNames) { ConverterDeck converterDeck = new ConverterDeck(deckSectionNames); ConverterSection mtgMainDeckConverterSection = converterDeck.ConverterSections.First(cs => cs.SectionName.Equals("Main", StringComparison.InvariantCultureIgnoreCase)); ConverterSection mtgSideboardConverterSection = converterDeck.ConverterSections.First(cs => cs.SectionName.Equals("Sideboard", StringComparison.InvariantCultureIgnoreCase)); foreach (string line in lines) { // The line is the Deck Name? Record it. string potentialDeckName = TextConverter.RegexMatch_DeckName(line); if (potentialDeckName != null) { converterDeck.DeckName = potentialDeckName; } else { // Ordering: The most specific pattern is listed first, and each more generalized pattern follows if (RegexMatch_MWSSideBoardCard(line) != null) { // The line is a MWS sideboard "SB: Quantity [SET] Card" entry mtgSideboardConverterSection.AddConverterMapping(RegexMatch_MWSSideBoardCard(line)); } else if (RegexMatch_MWSMainDeckCard(line) != null) { // The line is a MWS main deck "Quantity [SET] Card" entry mtgMainDeckConverterSection.AddConverterMapping(RegexMatch_MWSMainDeckCard(line)); } else if (RegexMatch_RegularMTGSideBoardCard(line) != null) { // The line is a regular sideboard "Quantity Card" entry, without any Set info mtgSideboardConverterSection.AddConverterMapping(RegexMatch_RegularMTGSideBoardCard(line)); } else { ConverterMapping potentialCard = TextConverter.ParseLineForCardAndQuantity(line); if (potentialCard != null) { mtgMainDeckConverterSection.AddConverterMapping(potentialCard); } else { // The line is not a valid card entry } } } } converterDeck.ConversionSuccessful = true; return(converterDeck); }
public void SerializeAndDeserializeAType() { System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); ConverterSection section = new ConverterSection(); section.Type = typeof(Exception); config.Sections.Add(sectionName, section); config.Save(); config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); section = config.Sections[sectionName] as ConverterSection; Assert.IsNotNull(section); Assert.AreEqual(section.Type, typeof(Exception)); }
public void SerializeAndDeserialzieANullByteArray() { SysConfig.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); ConverterSection section = new ConverterSection(); section.ByteArray = null; config.Sections.Add(sectionName, section); config.Save(); config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); section = config.Sections[sectionName] as ConverterSection; Assert.IsNotNull(section); Assert.IsNull(section.ByteArray); }
public void SerializeAndDeserialzieANullByteArray() { System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); ConverterSection section = new ConverterSection(); section.ByteArray = null; config.Sections.Add(sectionName, section); config.Save(); config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); section = config.Sections[sectionName] as ConverterSection; Assert.IsNotNull(section); Assert.IsNull(section.ByteArray); }
public void SerializeAndDeserializeAByteArray() { SysConfig.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); ConverterSection section = new ConverterSection(); section.ByteArray = new byte[] { 1, 2, 3, 4 }; config.Sections.Add(sectionName, section); config.Save(); config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); section = config.Sections[sectionName] as ConverterSection; Assert.IsNotNull(section); byte actual = 1; Assert.AreEqual(section.ByteArray[0], actual); }
/// <summary> /// Reads the lines of text which is in the SpellBookBuilder Text format, and returns a ConverterDeck which is populated with all cards and deck name. /// </summary> /// <param name="lines">The lines of text of the Deck file to convert</param> /// <param name="deckSectionNames">List of the name of each section for the deck being converted.</param> /// <returns>A ConverterDeck object populated with all cards and deck name</returns> public ConverterDeck Convert(IEnumerable <string> lines, IEnumerable <string> deckSectionNames) { string deckName = lines.FirstOrDefault(l => SpellBookBuilderText.RegexMatch_DeckName(l) != null); string mage; try { mage = (from l in lines select SpellBookBuilderText.RegexMatch_Mage(l)).First(m => m != null); } catch (Exception e) { e.Data.Add("Description", "Could not find a Mage in this deck."); throw; } List <ParsedCard> cards = new List <ParsedCard>(); foreach (string line in lines) { ParsedCard potentialCard = SpellBookBuilderText.RegexMatch_SBBTCard(line); if (potentialCard != null) { cards.Add(potentialCard); } } ConverterDeck converterDeck = new ConverterDeck(deckSectionNames); // Insert the Deck Name if (deckName != null) { converterDeck.DeckName = deckName; } // Add the Mage with quantity of 1 ConverterSection mageSection = converterDeck.ConverterSections.First(s => s.SectionName.Equals("Mage", StringComparison.InvariantCultureIgnoreCase)); mageSection.AddConverterMapping(new ConverterMapping(mage, string.Empty, 1)); // Add each other card with quantity to the corresponding Section foreach (ParsedCard card in cards) { ConverterSection correspondingSection = converterDeck.ConverterSections.First(s => s.SectionName.Equals(card.Section, StringComparison.InvariantCultureIgnoreCase)); correspondingSection.AddConverterMapping(new ConverterMapping(card.Name, string.Empty, card.Quantity)); } return(converterDeck); }
public void SerializeAndDeserializeAByteArray() { System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); ConverterSection section = new ConverterSection(); section.ByteArray = new byte[] { 1, 2, 3, 4 }; config.Sections.Add(sectionName, section); config.Save(); config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); section = config.Sections[sectionName] as ConverterSection; Assert.IsNotNull(section); byte actual = 1; Assert.AreEqual(section.ByteArray[0], actual); }
/// <summary> /// Reads the OCTGN format file, and returns a ConverterDeck which is populated with all cards. /// </summary> /// <param name="fullPathName">The full path name of the Deck file to convert</param> /// <param name="deckSectionNames">List of the name of each section for the deck being converted.</param> /// <returns>A ConverterDeck object populated with all cards and deck name</returns> /// <remarks>This will purposely ignore the Guids, which may help importing from OCTGN2 or other unofficial sets</remarks> public override ConverterDeck Convert(string fullPathName, IEnumerable <string> deckSectionNames) { ConverterDeck converterDeck = new ConverterDeck(deckSectionNames); using (XmlTextReader reader = new XmlTextReader(fullPathName)) { reader.WhitespaceHandling = WhitespaceHandling.None; XmlDocument xd = new XmlDocument(); xd.Load(reader); XmlNode xnodDE = xd.DocumentElement; if (xnodDE.Name != "deck") { throw new InvalidOperationException("File is not a Octgn Deck"); } foreach (XmlNode child in xnodDE.ChildNodes) { if (child.Name.Equals("section", StringComparison.InvariantCultureIgnoreCase)) { ConverterSection converterSection = converterDeck.ConverterSections.First(cs => cs.SectionName.Equals(child.Attributes.GetNamedItem("name").Value, StringComparison.InvariantCultureIgnoreCase)); foreach (XmlNode cardXmlNode in child.ChildNodes) { converterSection.AddConverterMapping ( new ConverterMapping ( cardXmlNode.InnerText, string.Empty, int.Parse(cardXmlNode.Attributes.GetNamedItem("qty").Value) ) ); } } } } converterDeck.ConversionSuccessful = true; return(converterDeck); }
/// <summary> /// Converts a LoTR URL from cardgamedb.com into a ConverterDeck which is populated with all cards and deck name. /// </summary> /// <param name="url">The URL of the Deck</param> /// <param name="deckSectionNames">List of the name of each section for the deck being converted.</param> /// <param name="convertGenericFileFunc"> /// Function to convert a collection of lines from a deck file into a ConverterDeck. /// Used when downloading a Deck File from a webpage instead of scraping. /// </param> /// <returns>A ConverterDeck which is populated with all cards and deck name</returns> public override ConverterDeck Convert( string url, IEnumerable <string> deckSectionNames, Func <IEnumerable <string>, IEnumerable <string>, ConverterDeck> convertGenericFileFunc) { object htmlWebInstance = HtmlAgilityPackWrapper.HtmlWeb_CreateInstance(); object htmlDocumentInstance = HtmlAgilityPackWrapper.HtmlWeb_InvokeMethod_Load(htmlWebInstance, url); object htmlDocument_DocumentNode = HtmlAgilityPackWrapper.HtmlDocument_GetProperty_DocumentNode(htmlDocumentInstance); // Find the block of javascript that contains the variable 'viewType' object rawDeckJavascriptNode = HtmlAgilityPackWrapper.HtmlNode_InvokeMethod_SelectSingleNode(htmlDocument_DocumentNode, "//script[contains(text(), 'var viewType =')]"); string rawDeckJavascriptText = HtmlAgilityPackWrapper.HtmlNode_GetProperty_InnerText(rawDeckJavascriptNode); string[] rawDeckJavascriptLines = rawDeckJavascriptText.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); dynamic rawDeckJSON = null; string viewTypeLine = rawDeckJavascriptLines.FirstOrDefault(l => l.Contains("var viewType =")); if (viewTypeLine == null) { throw new InvalidOperationException("Could not find the javascript variable 'viewType'"); } else if (viewTypeLine.Contains("submitted")) { // Since viewType is 'submitted', the deck is published. In this case, the JSON data the deck is embedded on this page in the javascript variable 'rawdeck' string rawDeckLine = rawDeckJavascriptLines.First(l => l.Contains("var rawdeck =")); // Trim everything except the JSON int openingCurlyBraceIndex = rawDeckLine.IndexOf('{'); int closingCurlyBraceIndex = rawDeckLine.LastIndexOf('}'); string rawDeckJSONString = rawDeckLine.Substring(openingCurlyBraceIndex, closingCurlyBraceIndex - openingCurlyBraceIndex + 1); rawDeckJSON = JsonConvert.DeserializeObject(rawDeckJSONString); } else if (viewTypeLine.Contains("share")) { // Since viewType is 'share', the deck is personal. In this case, the JSON is not embedded on this page and must be fetched via a form POST Dictionary <string, string> urlParams = WebpageConverter.GetParams(url); // Find the block of javascript that contains the 'ipb.vars' dictionary variable object rawIPBVarsJavascriptNode = HtmlAgilityPackWrapper.HtmlNode_InvokeMethod_SelectSingleNode(htmlDocument_DocumentNode, "//script[contains(text(), 'ipb.vars[')]"); string rawIPBVarsJavascriptText = HtmlAgilityPackWrapper.HtmlNode_GetProperty_InnerText(rawIPBVarsJavascriptNode); string[] rawIPBVarsJavascriptLines = rawIPBVarsJavascriptText.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); // Extract the 'secure_hash' value from IPBVar string secureHashLine = rawIPBVarsJavascriptLines.First(l => l.Contains(@"ipb.vars['secure_hash']")); string secure_hash = secureHashLine.Substring(0, secureHashLine.LastIndexOf('\'')); secure_hash = secure_hash.Substring(secure_hash.LastIndexOf('\'') + 1); // Extract the 'secure_hash' value from IPBVar string baseURLLine = rawIPBVarsJavascriptLines.First(l => l.Contains(@"ipb.vars['base_url']")); string base_url = baseURLLine.Substring(0, baseURLLine.LastIndexOf('\'')); base_url = base_url.Substring(base_url.LastIndexOf('\'') + 1); // These variables are all submitted with the form. They are added via javascript in lotrdecksection.js System.Collections.Specialized.NameValueCollection outgoingQueryString = System.Web.HttpUtility.ParseQueryString(String.Empty); outgoingQueryString.Add("dguid", urlParams["deck"]); outgoingQueryString.Add("fPage", "deckshare"); outgoingQueryString.Add("fgame", "lordoftherings"); outgoingQueryString.Add("md5check", secure_hash); outgoingQueryString.Add("pid", urlParams["p"]); string postData = outgoingQueryString.ToString(); ASCIIEncoding ascii = new ASCIIEncoding(); byte[] postBytes = ascii.GetBytes(postData.ToString()); string requestString = base_url + "app=ccs&module=ajax§ion=lotrdeckbuilder&do=share"; System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(requestString); request.Method = "POST"; request.Accept = "application/json"; request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = postBytes.Length; request.Host = "www.cardgamedb.com"; request.Referer = url; request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0"; // add post data to request System.IO.Stream postStream = request.GetRequestStream(); postStream.Write(postBytes, 0, postBytes.Length); postStream.Flush(); postStream.Close(); System.Net.WebResponse response = request.GetResponse(); System.IO.Stream dataStream = response.GetResponseStream(); System.IO.StreamReader reader = new System.IO.StreamReader(dataStream); string responseFromServer = System.Web.HttpUtility.HtmlDecode(reader.ReadToEnd()); reader.Close(); response.Close(); rawDeckJSON = JsonConvert.DeserializeObject(responseFromServer); // When JSON is retrieved this way, the deck contents is stored in a sub-variable rawDeckJSON = rawDeckJSON.deckContents; } else { throw new InvalidOperationException("The javascript variable 'viewType' was not an expected value (" + viewTypeLine + ")"); } ConverterDeck converterDeck = new ConverterDeck(deckSectionNames); foreach (dynamic card in rawDeckJSON.hero) { ConverterSection converterSection = converterDeck.ConverterSections.First(cs => cs.SectionName.Equals("hero", StringComparison.InvariantCultureIgnoreCase)); ConverterMapping converterMapping = new ConverterMapping( (string)card.name, (string)card.setname, int.Parse((string)card.quantity)); converterSection.AddConverterMapping(converterMapping); } foreach (dynamic card in rawDeckJSON.cards) { ConverterSection converterSection = converterDeck.ConverterSections.First(cs => cs.SectionName.Equals((string)card.type, StringComparison.InvariantCultureIgnoreCase)); ConverterMapping converterMapping = new ConverterMapping( (string)card.name, (string)card.setname, int.Parse((string)card.quantity)); converterSection.AddConverterMapping(converterMapping); } return(converterDeck); }
/// <summary> /// Converts a MW URL from arcanewonders.com into a ConverterDeck which is populated with all cards and deck name. /// </summary> /// <param name="url">The URL of the Deck</param> /// <param name="deckSectionNames">List of the name of each section for the deck being converted.</param> /// <param name="convertGenericFileFunc"> /// Function to convert a collection of lines from a deck file into a ConverterDeck. /// Used when downloading a Deck File from a webpage instead of scraping. /// </param> /// <returns>A ConverterDeck which is populated with all cards and deck name</returns> public override ConverterDeck Convert( string url, IEnumerable <string> deckSectionNames, Func <IEnumerable <string>, IEnumerable <string>, ConverterDeck> convertGenericFileFunc) { object htmlWebInstance = HtmlAgilityPackWrapper.HtmlWeb_CreateInstance(); object htmlDocumentInstance = HtmlAgilityPackWrapper.HtmlWeb_InvokeMethod_Load(htmlWebInstance, url); object htmlDocument_DocumentNode = HtmlAgilityPackWrapper.HtmlDocument_GetProperty_DocumentNode(htmlDocumentInstance); ConverterDeck converterDeck = new ConverterDeck(deckSectionNames); // Find the div with id 'spellbook' because all deck data is contained inside it object spellbookDiv = HtmlAgilityPackWrapper.HtmlNode_InvokeMethod_SelectSingleNode(htmlDocument_DocumentNode, "//div[@id='spellbook']"); if (spellbookDiv == null) { throw new InvalidOperationException("Could not find the html div 'spellbook', are you sure this webpage has a deck?"); } // Insert the Deck Name if available object spellbooknameSpan = HtmlAgilityPackWrapper.HtmlNode_InvokeMethod_SelectSingleNode(spellbookDiv, "//span[@id='spellbookname']"); if (spellbooknameSpan != null) { converterDeck.DeckName = HtmlAgilityPackWrapper.HtmlNode_GetProperty_InnerText(spellbooknameSpan); } // Convert the Mage card object firstMageSpan = HtmlAgilityPackWrapper.HtmlNode_InvokeMethod_SelectNodes(spellbookDiv, "//span[@id='mage']").FirstOrDefault(); if (firstMageSpan != null) { ConverterSection mageSection = converterDeck.ConverterSections.First(cs => cs.SectionName.Equals("Mage", StringComparison.InvariantCultureIgnoreCase)); mageSection.AddConverterMapping(new ConverterMapping( HtmlAgilityPackWrapper.HtmlNode_GetProperty_InnerText(firstMageSpan), string.Empty, 1)); } // The div 'spells' contains all the cards, grouped by spell class object spellsDiv = HtmlAgilityPackWrapper.HtmlNode_InvokeMethod_SelectSingleNode(spellbookDiv, "//div[@id='spells']"); // Get a collection of all the span nodes inside the 'spells' div IEnumerable <object> cardAndSectionSpans = HtmlAgilityPackWrapper.HtmlNode_InvokeMethod_SelectNodes(spellsDiv, "span"); ConverterSection currentConverterSection = null; foreach (object cardOrSectionSpan in cardAndSectionSpans) { // get the class name of the span IEnumerable <object> attributes = HtmlAgilityPackWrapper.HtmlNode_GetProperty_Attributes(cardOrSectionSpan); string className = string.Empty; foreach (object attribute in attributes) { if (HtmlAgilityPackWrapper.HtmlAttribute_GetProperty_Name(attribute).Equals("class", StringComparison.InvariantCultureIgnoreCase)) { className = HtmlAgilityPackWrapper.HtmlAttribute_GetProperty_Value(attribute); break; } } // If the class name of the span is 'spellClass', then it denotes a Deck Section. Find the corresponding ConverterSection if (className.Equals("spellClass", StringComparison.InvariantCultureIgnoreCase)) { string currentSpellClass = HtmlAgilityPackWrapper.HtmlNode_GetProperty_InnerText(cardOrSectionSpan); currentConverterSection = converterDeck.ConverterSections.First(cs => cs.SectionName.Equals(currentSpellClass, StringComparison.InvariantCultureIgnoreCase)); } else { // This span contains a card and quantity, so parse it string quantityAndCardString = HtmlAgilityPackWrapper.HtmlNode_GetProperty_InnerText(cardOrSectionSpan); ConverterMapping converterMapping = TextConverter.RegexMatch_RegularCard(quantityAndCardString); currentConverterSection.AddConverterMapping(converterMapping); } } return(converterDeck); }