/// <summary> /// Exports a xoreos xml (export) to yml. /// </summary> /// <param name="infile"></param> private static void Xml2Yml(string infile) { #region Save Settings var filename = Path.GetFileNameWithoutExtension(infile); var fileDirectory = Path.GetDirectoryName(infile); //var newDirectory = Path.Combine(fileDirectory, $"out/{filename}"); var ymlDirectory = Path.Combine(fileDirectory, $"out"); if (!Directory.Exists(ymlDirectory)) { Directory.CreateDirectory(ymlDirectory); } //string outfile = Path.Combine(newDirectory, $"{filename}_out.xml"); //string outfile_sections = Path.Combine(newDirectory, $"{filename}_sections.xml"); string outfile_yml = Path.Combine(ymlDirectory, $"{filename}.yml"); #endregion gff3struct parsedClass = gff3Reader.Read(infile); var gffWriter = new gff3Writer(parsedClass, infile); gffWriter.GenerateXML(); //gffWriter.XDOC.Save(outfile); //dbg gffWriter.GenerateSectionsXML(); //gffWriter.XDOC_SECTIONS.Save(outfile_sections); //dbg ymlWriter.Write(outfile_yml, gffWriter.XDOC_SECTIONS); }
/// <summary> /// /// </summary> /// <param name="data"></param> /// <param name="idx"></param> /// <returns></returns> private SDialogscriptdata AttributeData(gff3struct data, ref List <XAttribute> ret) { var sdata = new SDialogscriptdata(); //MISC //ret.Add(new XAttribute("Interlocutor", data.GetCommonObjectByName("Interlocutor").Value.ToString())); //ret.Add(new XAttribute("NodeType", data.GetCommonObjectByName("NodeType").Value.ToString())); //ret.Add(new XAttribute("Id", data.GetCommonObjectByName("Id").Value.ToString())); string qf = data.GetCommonObjectByName("Quest")?.Value?.ToString(); bool isquest = !string.IsNullOrEmpty(qf); if (isquest) { //remove special characters qf = qf.Replace(' ', '_'); Regex rgx = new Regex("[^a-zA-Z0-9_]"); qf = rgx.Replace(qf, "").ToLower(); ret.Add(new XAttribute("Quest", qf)); sdata.questfact = qf; } return(sdata); }
/// <summary> /// /// </summary> /// <param name="gff3"></param> /// <returns></returns> public void GenerateXML() { #region XML Structure var Doc = new XDocument(); var root = new XElement("root"); Doc.AddFirst(root); Doc.Root.Add( new XElement("Settings"), new XElement("Dialogscripts")); XElement Dialogscripts = Doc.Descendants("Dialogscripts").First(); XElement Settings = Doc.Descendants("Settings").First(); Settings.Add(new XElement("Actors")); Settings.Add(new XElement("Player")); Settings.Add(new XElement("SectionsList")); #endregion // Speaker settings List <gff3struct> SpeakerList = ((CGff3ListObject <gff3struct>)GFF.GetGenericObjectByName("SpeakerList"))?.Value; List <string> Actors = SpeakerList.Select(x => x.GetCommonObjectByName("Speaker")?.Value.ToString())?.ToList(); foreach (var actor in Actors) { Settings.Element("Actors").Add(new XElement("Actor", actor)); } //Dialogscripts CGff3ListObject <gff3struct> StartingList = (CGff3ListObject <gff3struct>)GFF.GetGenericObjectByName("StartingList"); for (int i = 0; i < StartingList.Value.Count; i++) { //add generic start sections to section list // FIXME pause? var startelement = new XElement("PAUSE", new XAttribute("section", $"section_start_{i}"), new XAttribute("ref", $"start_{i}") ); Dialogscripts.Add(startelement); Settings.Element("SectionsList").Add(new XElement("Section", $"start_{i}", new XAttribute("Name", $"section_start_{i}"))); // gff3struct item = (gff3struct)StartingList.Value[i]; CGff3GenericObject obj = item.GetCommonObjectByName("Index"); int idx = int.Parse(obj.Value.ToString()); // Write Tree WriteTree(GFF, startelement, idx, "entry", true); } //Modify Speaker Section string player = Settings.Element("Player").Value; List <string> actors = Settings.Element("Actors").Descendants()?.Select(x => x.Value).ToList(); if (String.IsNullOrEmpty(player)) { Settings.Element("Player").Value = actors.First(); } XDOC = Doc; }
/// <summary> /// pass the xstruct node, return a gff3 struct /// </summary> /// <param name="in_xstruct"></param> /// <param name="parentStruct"></param> private static void ParseStruct(XElement in_xstruct, gff3struct parentStruct) { string structID = in_xstruct.Attribute("id").Value; foreach (var node in in_xstruct.Elements()) { // if node is a list if (ReadType(node) == "list") { //get variables var label = ReadLabel(node); var type = ReadType(node); var children = new List <gff3struct>(); var xchildren = node.Elements(); //nodes with name struct id=0, id=1 etc... foreach (var childnode in xchildren) { var schild = new gff3struct(ReadID(childnode)); ParseStruct(childnode, schild); //passes the struct children.Add(schild); } //create object and add to parent struct var obj = new CGff3List(type, label, children); parentStruct.Data.Add(obj); } // if node is a common type else { if (ReadType(node) == "locstring") { var label = ReadLabel(node); var type = ReadType(node); var children = new List <CGff3String>(); //strref var xchildren = node.Elements(); foreach (var childnode in xchildren) { CGff3String xstring = ReadString(childnode); children.Add(xstring); } var obj = new CGff3Locstring(type, label, children); parentStruct.Data.Add(obj); } // add common type to paremt struct else { parentStruct.Data.Add(ReadGeneric(node)); } } } }
/// <summary> /// Reads an xml and returns a gff3 struct. /// </summary> /// <param name="path"></param> /// <returns></returns> public static gff3struct Read(string path) { var doc = XDocument.Load(path); XElement in_xstruct = doc.Element("gff3").Element("struct"); gff3struct parentStruct = new gff3struct(ReadID(in_xstruct)); ParseStruct(in_xstruct, parentStruct); return(parentStruct); }
private List <string> HACK_Allrefs = new List <string>(); //HACK //FIXME public gff3Writer(gff3struct gff, string name) { GFF = gff; Name = name; }
/// <summary> /// Prints custom data to xml element /// </summary> /// <param name="data"></param> /// <param name="parent"></param> /// <param name="idx"></param> /// <param name="type"></param> /// <returns></returns> private bool AnnotateXML(gff3struct data, XElement parent, int idx, string type, bool isSection, ref XElement output) { //Document Settings var Doc = parent.Document; XElement Actors = Doc.Descendants("Settings").First()?.Element("Actors"); XElement Player = Doc.Descendants("Settings").First()?.Element("Player"); List <XAttribute> attributes = new List <XAttribute>(); //attributes.Add(new XAttribute("Idx", idx)); output.Add(new XAttribute("ref", $"{type}_{idx}")); //TEXT CGff3Locstring locstring = (CGff3Locstring)data.GetListObjectByName <CGff3String>("Text"); string Text = ""; if (locstring != null) { CGff3String englishText = locstring.Value.FirstOrDefault(x => x.Language == "6"); if (englishText != null) { Text = englishText.Value; } } attributes.Add(new XAttribute("Text", Text)); //SPEAKER string Speaker = data.GetCommonObjectByName("Speaker").Value.ToString(); if (Speaker == "__player__") { Speaker = "geralt"; } if (Speaker == "__owner__") { Speaker = "npc"; } //write speakers to settings if (Actors == null) { throw new Gff3Exception("No Actors in Database"); //should never go offbut keep it for testing } List <string> ActorList = Actors.Elements().Select(x => x.Value.ToString()).ToList(); //Add actors to actorlist if (!ActorList.Contains(Speaker)) { Actors.Add(new XElement("Actor", Speaker)); } if (Speaker == "geralt" && Player.Value != null) { Player.Value = Speaker; } attributes.Add(new XAttribute("Speaker", Speaker)); // Adding soundfile var string Sound = data.GetCommonObjectByName("Sound").Value.ToString(); attributes.Add(new XAttribute("Sound", Sound)); //OTHER var dsdata = AttributeData(data, ref attributes); //QUESTS //ADD output.Add(attributes.ToArray()); //add section data if (isSection) { //remove special charactersand to lower string sectionname = Text.Split(' ').First(); Regex rgx = new Regex("[^a-zA-Z0-9]"); sectionname = $"section_{rgx.Replace(sectionname, "").ToLower()}_{idx}"; //add to sections list AddToSectionsNoDuplicates(Doc, sectionname, $"{type}_{idx}"); output.Add(new XAttribute("section", sectionname)); } bool isQuestParent = !String.IsNullOrEmpty(dsdata.questfact); if (isQuestParent) { //add to quest list AddToSectionsNoDuplicates(Doc, $"script_setfact_{dsdata.questfact}", $"quest_{idx}"); //add quest xml tags var scriptelement = new XElement("SCRIPT", new XAttribute("function", "AddFact_S"), new XElement("parameter", new XAttribute("factName", $"{dsdata.questfact}"), new XAttribute("value", $"1"), new XAttribute("validFor", $"0") )); var questelement = new XElement("QUEST", new XAttribute("Name", dsdata.questfact), new XAttribute("section", $"script_setfact_{dsdata.questfact}"), new XAttribute("ref", $"quest_{idx}"), scriptelement ); // reshuffle nodes output.Add(questelement); parent.Add(output); output = output.Element("QUEST"); } else { parent.Add(output); } return(isQuestParent); }
/// <summary> /// Recursive writing dialogue tree. /// </summary> /// <param name="gff3"></param> /// <param name="idx"></param> /// <param name="type"></param> private void WriteTree(gff3struct gff3, XElement printparent, int idx, string type, bool isSection) { //HACK //FIXME loop detection string HACK_ref = $"{type}_{idx}"; if (!HACK_Allrefs.Contains(HACK_ref)) { HACK_Allrefs.Add(HACK_ref); } else { throw new Gff3Exception("Looping"); } XElement output = new XElement(type); if (type == "entry") { gff3struct entry = gff3.GetEntryByIndex(idx); //PRINT DATA bool isquest = AnnotateXML(entry, printparent, idx, type, isSection, ref output); //get replies // there can be more than one reply // if there is more than one reply, this marks a CHOICE // and we must label all replys as goto points CGff3ListObject <gff3struct> replies = entry.GetListObjectByName <gff3struct>("RepliesList"); if (replies.Value.Count == 0) { output.Add(new XAttribute("END", "true")); } else if (replies.Value.Count > 1) { output.Add(new XAttribute("CHOICE", "true")); } //handle circular references if (EntryChoices.Contains(idx)) { return; } if (replies.Value.Count > 1) { EntryChoices.Add(idx); } foreach (gff3struct reply in replies.Value) { int newidx = int.Parse(reply.GetCommonObjectByName("Index").Value.ToString()); //if there is a choice, flag the two replies bool ischildSection = replies.Value.Count > 1 || isquest; WriteTree(gff3, output, newidx, "reply", ischildSection); } } else if (type == "reply") { gff3struct reply = gff3.GetReplyByIndex(idx); //PRINT DATA var isquest = AnnotateXML(reply, printparent, idx, type, isSection, ref output); //get entries // there can never be more than two entries // because nps can't choose CGff3ListObject <gff3struct> entries = reply.GetListObjectByName <gff3struct>("EntriesList"); if (entries.Value.Count == 0) { output.Add(new XAttribute("END", "true")); } else if (entries.Value.Count == 1) { int newidx = int.Parse(entries.Value.First().GetCommonObjectByName("Index").Value.ToString()); WriteTree(gff3, output, newidx, "entry", isquest); } else { throw new Gff3Exception("More than one Entry in a Reply"); } } }