/// <summary>
        /// Loads and parses quest markup data to QuestDocument.
        /// No additional logical structure checks, use "validate" method for it.
        /// </summary>
        /// <param name="markup">Markup source of quest data.</param>
        public static QuestDocument LoadMarkup(string markup)
        {
            if (string.IsNullOrEmpty(markup))
            {
                throw new Exception("Invalid markup data");
            }
            var stream = new QuestStream(markup);
            var doc    = new QuestDocument();

            while (!stream.Eof())
            {
                var    lineId = stream.pointer;
                string name;
                var    page = ParsePage(stream, out name);
                if (doc.pages.ContainsKey(name))
                {
                    throw new Exception(
                              string.Format("Error at {0} line : page with name \"{1}\" already declared before", lineId, name));
                }
                doc.pages[name] = page;
                if (string.IsNullOrEmpty(doc.entry))
                {
                    doc.entry = name;
                }
            }
            doc.ResetProgress();
            return(doc);
        }
 /// <summary>
 /// Validates QuestDocument for possible errors. Exception will be thrown on any detected error.
 /// </summary>
 /// <param name="doc">Document to validate.</param>
 public static void Validate(QuestDocument doc)
 {
     _linksToValidate.Clear();
     _linksToValidate[doc.entry] = true;
     foreach (var pagePair in doc.pages)
     {
         var page = pagePair.Value;
         if (page == null)
         {
             throw new Exception(string.Format("Invalid page with name \"{0}\"", pagePair.Key));
         }
         if (page.texts == null || page.texts.Count == 0)
         {
             throw new Exception(string.Format("Page \"{0}\": no texts", pagePair.Key));
         }
         if (page.choices == null || page.choices.Count == 0)
         {
             throw new Exception(string.Format("Page \"{0}\": no choices", pagePair.Key));
         }
         for (var i = 0; i < page.choices.Count; i++)
         {
             var link = page.choices[i].link;
             if (link != "end")
             {
                 if (!doc.pages.ContainsKey(link))
                 {
                     _linksToValidate.Clear();
                     throw new Exception(string.Format("Invalid link from page \"{0}\" to unknown \"{1}\"", pagePair.Key, link));
                 }
                 _linksToValidate[link] = true;
             }
         }
     }
     foreach (var pagePair in doc.pages)
     {
         if (!_linksToValidate.ContainsKey(pagePair.Key))
         {
             _linksToValidate.Clear();
             throw new Exception(string.Format("Page without references: \"{0}\"", pagePair.Key));
         }
     }
     _linksToValidate.Clear();
 }