From f81958679a9837e3dc124ec6bae09d2becd67c08 Mon Sep 17 00:00:00 2001 From: dexy Date: Fri, 17 Jan 2020 23:02:55 +1100 Subject: [PATCH] YTD/XML conversion --- .../GameFiles/FileTypes/YtdFile.cs | 65 +++ CodeWalker.Core/GameFiles/GameFileCache.cs | 2 +- .../GameFiles/MetaTypes/MetaXml.cs | 14 +- .../GameFiles/Resources/ResourceBaseTypes.cs | 2 +- .../GameFiles/Resources/Texture.cs | 493 ++++++++---------- ExploreForm.cs | 30 +- 6 files changed, 327 insertions(+), 279 deletions(-) diff --git a/CodeWalker.Core/GameFiles/FileTypes/YtdFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YtdFile.cs index 6f52cbf..c106460 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/YtdFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/YtdFile.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml; namespace CodeWalker.GameFiles { @@ -56,4 +57,68 @@ namespace CodeWalker.GameFiles } + + + + + public class YtdXml : MetaXmlBase + { + + public static string GetXml(YtdFile ytd, string outputFolder = "") + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(XmlHeader); + + var ddsfolder = outputFolder; + if (!string.IsNullOrEmpty(ddsfolder)) + { + ddsfolder = Path.Combine(outputFolder, ytd.Name); + + if (!Directory.Exists(ddsfolder)) + { + Directory.CreateDirectory(ddsfolder); + } + } + + if (ytd?.TextureDict != null) + { + TextureDictionary.WriteXmlNode(ytd.TextureDict, sb, 0, ddsfolder); + } + + return sb.ToString(); + } + + } + + public class XmlYtd + { + + public static YtdFile GetYtd(string xml, string inputFolder = "") + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + return GetYtd(doc, inputFolder); + } + + public static YtdFile GetYtd(XmlDocument doc, string inputFolder = "") + { + YtdFile r = new YtdFile(); + + var ddsfolder = inputFolder; + + var node = doc.DocumentElement; + if (node != null) + { + r.TextureDict = TextureDictionary.ReadXmlNode(node, ddsfolder); + } + + r.Name = Path.GetFileName(inputFolder); + + return r; + } + + } + + + } diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs index e289f9e..288e9b8 100644 --- a/CodeWalker.Core/GameFiles/GameFileCache.cs +++ b/CodeWalker.Core/GameFiles/GameFileCache.cs @@ -3440,7 +3440,7 @@ namespace CodeWalker.GameFiles } public void TestYtds() { - bool ddstest = true; + bool ddstest = false; bool savetest = false; var errorfiles = new List(); foreach (RpfFile file in AllRpfs) diff --git a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs index d2a80c2..ca999b5 100644 --- a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs +++ b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs @@ -12,7 +12,7 @@ namespace CodeWalker.GameFiles public class MetaXml : MetaXmlBase { - public static string GetXml(RpfFileEntry e, byte[] data, out string filename) + public static string GetXml(RpfFileEntry e, byte[] data, out string filename, string outputfolder = "") { var fn = e.Name; var fnl = fn.ToLowerInvariant(); @@ -66,6 +66,11 @@ namespace CodeWalker.GameFiles YbnFile ybn = RpfFile.GetFile(e, data); return GetXml(ybn, out filename); } + else if (fnl.EndsWith(".ytd")) + { + YtdFile ytd = RpfFile.GetFile(e, data); + return GetXml(ytd, out filename, outputfolder); + } filename = fn; return string.Empty; } @@ -143,6 +148,12 @@ namespace CodeWalker.GameFiles filename = fn + ".xml"; return YbnXml.GetXml(ybn); } + public static string GetXml(YtdFile ytd, out string filename, string outputfolder) + { + var fn = (ytd?.Name) ?? ""; + filename = fn + ".xml"; + return YtdXml.GetXml(ytd, outputfolder); + } @@ -2029,6 +2040,7 @@ namespace CodeWalker.GameFiles Ynd = 6, Ycd = 7, Ybn = 8, + Ytd = 9, } } diff --git a/CodeWalker.Core/GameFiles/Resources/ResourceBaseTypes.cs b/CodeWalker.Core/GameFiles/Resources/ResourceBaseTypes.cs index e9e24c0..af05315 100644 --- a/CodeWalker.Core/GameFiles/Resources/ResourceBaseTypes.cs +++ b/CodeWalker.Core/GameFiles/Resources/ResourceBaseTypes.cs @@ -1062,7 +1062,7 @@ namespace CodeWalker.GameFiles public ushort EntriesCapacity { get; private set; } // reference data - public uint[] data_items { get; private set; } + public uint[] data_items { get; set; } private ResourceSystemStructBlock data_block;//used for saving. diff --git a/CodeWalker.Core/GameFiles/Resources/Texture.cs b/CodeWalker.Core/GameFiles/Resources/Texture.cs index 509e62d..b8edcde 100644 --- a/CodeWalker.Core/GameFiles/Resources/Texture.cs +++ b/CodeWalker.Core/GameFiles/Resources/Texture.cs @@ -1,9 +1,11 @@ -using System; +using CodeWalker.Utils; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml; namespace CodeWalker.GameFiles { @@ -29,7 +31,6 @@ namespace CodeWalker.GameFiles public Dictionary Dict { get; set; } - public long MemoryUsage { get @@ -49,15 +50,10 @@ namespace CodeWalker.GameFiles } } - public TextureDictionary() - { - //this.TextureNameHashes = new ResourceSimpleList64_uint(); - //this.Textures = new ResourcePointerList64(); - } - /// - /// Reads the data-block from a stream. - /// + public TextureDictionary() + { } + public override void Read(ResourceDataReader reader, params object[] parameters) { base.Read(reader, parameters); @@ -70,22 +66,8 @@ namespace CodeWalker.GameFiles this.TextureNameHashes = reader.ReadBlock(); this.Textures = reader.ReadBlock>(); - var dict = new Dictionary(); - if ((Textures?.data_items != null) && (TextureNameHashes?.data_items != null)) - { - for (int i = 0; (i < Textures.data_items.Length) && (i < TextureNameHashes.data_items.Length); i++) - { - var tex = Textures.data_items[i]; - var hash = TextureNameHashes.data_items[i]; - dict[hash] = tex; - } - } - Dict = dict;// new Dictionary(dict); + BuildDict(); } - - /// - /// Writes the data-block to a stream. - /// public override void Write(ResourceDataWriter writer, params object[] parameters) { base.Write(writer, parameters); @@ -99,6 +81,65 @@ namespace CodeWalker.GameFiles writer.WriteBlock(this.TextureNameHashes); writer.WriteBlock(this.Textures); } + public void WriteXml(StringBuilder sb, int indent, string ddsfolder) + { + + if (Textures?.data_items != null) + { + foreach (var tex in Textures.data_items) + { + YtdXml.OpenTag(sb, indent, "Item"); + tex.WriteXml(sb, indent + 1, ddsfolder); + YtdXml.CloseTag(sb, indent, "Item"); + } + } + + } + public void ReadXml(XmlNode node, string ddsfolder) + { + var textures = new List(); + var texturehashes = new List(); + + var inodes = node.SelectNodes("Item"); + if (inodes != null) + { + foreach (XmlNode inode in inodes) + { + var tex = new Texture(); + tex.ReadXml(inode, ddsfolder); + textures.Add(tex); + texturehashes.Add(tex.NameHash); + } + } + + TextureNameHashes = new ResourceSimpleList64_uint(); + TextureNameHashes.data_items = texturehashes.ToArray(); + Textures = new ResourcePointerList64(); + Textures.data_items = textures.ToArray(); + BuildDict(); + } + public static void WriteXmlNode(TextureDictionary d, StringBuilder sb, int indent, string ddsfolder, string name = "TextureDictionary") + { + if (d == null) return; + if ((d.Textures?.data_items == null) || (d.Textures.data_items.Length == 0)) + { + YtdXml.SelfClosingTag(sb, indent, name); + } + else + { + YtdXml.OpenTag(sb, indent, name); + d.WriteXml(sb, indent + 1, ddsfolder); + YtdXml.CloseTag(sb, indent, name); + } + } + public static TextureDictionary ReadXmlNode(XmlNode node, string ddsfolder) + { + if (node == null) return null; + var td = new TextureDictionary(); + td.ReadXml(node, ddsfolder); + return td; + } + public override Tuple[] GetParts() { @@ -118,20 +159,21 @@ namespace CodeWalker.GameFiles return tex; } - //public Dictionary GetDictionary() - //{ - // Dictionary td = new Dictionary(); - // if ((Textures != null) && (Textures.data_items != null)) - // { - // var texs = Textures.data_items; - // var hashes = TextureNameHashes; - // for (int i = 0; (i < texs.Length) && (i < hashes.Length); i++) - // { - // td.Add(hashes[i], texs[i]); - // } - // } - // return td; - //} + private void BuildDict() + { + var dict = new Dictionary(); + if ((Textures?.data_items != null) && (TextureNameHashes?.data_items != null)) + { + for (int i = 0; (i < Textures.data_items.Length) && (i < TextureNameHashes.data_items.Length); i++) + { + var tex = Textures.data_items[i]; + var hash = TextureNameHashes.data_items[i]; + dict[hash] = tex; + } + } + Dict = dict; + } + } [TypeConverter(typeof(ExpandableObjectConverter))] public class TextureBase : ResourceSystemBlock @@ -165,9 +207,6 @@ namespace CodeWalker.GameFiles private string_r NameBlock = null; - /// - /// Reads the data-block from a stream. - /// public override void Read(ResourceDataReader reader, params object[] parameters) { // read structure data @@ -214,10 +253,6 @@ namespace CodeWalker.GameFiles break; } } - - /// - /// Writes the data-block to a stream. - /// public override void Write(ResourceDataWriter writer, params object[] parameters) { // update structure data @@ -241,10 +276,19 @@ namespace CodeWalker.GameFiles writer.Write(this.Unknown_38h); writer.Write(this.Unknown_3Ch); } + public virtual void WriteXml(StringBuilder sb, int indent, string ddsfolder) + { + YtdXml.StringTag(sb, indent, "Name", Name); + YtdXml.ValueTag(sb, indent, "Unk32", Unknown_32h.ToString()); + } + public virtual void ReadXml(XmlNode node, string ddsfolder) + { + Name = Xml.GetChildInnerText(node, "Name"); + NameHash = JenkHash.GenHash(Name?.ToLowerInvariant()); + Unknown_32h = (ushort)Xml.GetChildUIntAttribute(node, "Unk32", "value"); + } + - /// - /// Returns a list of data blocks which are referenced by this block. - /// public override IResourceBlock[] GetReferences() { var list = new List(); @@ -317,35 +361,6 @@ namespace CodeWalker.GameFiles } } - public byte Unknown_40h - { - get - { - return (byte)(UsageData & 0xFF); - } - } - public byte Unknown_41h - { - get - { - return (byte)((UsageData >> 8) & 0xFF); - } - } - public byte Unknown_42h - { - get - { - return (byte)((UsageData >> 16) & 0xFF); - } - } - public byte Unknown_43h - { - get - { - return (byte)((UsageData >> 24) & 0xFF); - } - } - // reference data public TextureData Data { get; set; } @@ -363,9 +378,6 @@ namespace CodeWalker.GameFiles } } - /// - /// Reads the data-block from a stream. - /// public override void Read(ResourceDataReader reader, params object[] parameters) { base.Read(reader, parameters); @@ -396,177 +408,59 @@ namespace CodeWalker.GameFiles this.Unknown_8Ch = reader.ReadUInt32(); // read reference data - this.Data = reader.ReadBlockAt( - this.DataPointer, // offset - this.Format, - this.Width, - this.Height, - this.Levels, - this.Stride - ); + this.Data = reader.ReadBlockAt(this.DataPointer, this.Format, this.Width, this.Height, this.Levels, this.Stride); - - switch (Unknown_40h) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 8: - case 9: - case 10: - case 11: - case 12: - case 14: - case 16: - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - case 24: - case 25: - case 26: - case 38: - case 52: - case 54: - case 55: - case 66: - case 84: - case 86: - case 87: - case 116: - case 118: - case 119: - break; - case 32://embedded only - break; - default: - break; - } - switch (Unknown_41h) - { - case 0: - case 1: - case 2: - case 4: - case 6: - case 8: - case 0xA: - case 0xC: - case 0xE: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x16: - case 0x17: - case 0x18: - case 0x1C: - case 0x1E: - case 0x20: - case 0x22: - case 0x28: - case 0x2B: - case 0x2C: - case 0x30: - case 0x38: - case 0x39: - case 0x3C: - case 0x40: - case 0x4C: - case 0x4E: - case 0x50: - case 0x54: - case 0x56: - case 0x57: - case 0x58: - case 0x5A: - case 0x5C: - case 0x5E: - case 0x60: - case 0x64: - case 0x68: - case 0x70: - case 0x78: - case 0x80: - case 0x90: - case 0x9C: - case 0x9E: - case 0xA0: - case 0xA8: - case 0xAA: - case 0xAC: - case 0xAE: - case 0xB0: - case 0xB2: - case 0xB4: - case 0xB8: - case 0xBC: - case 0xC0: - case 0xD0: - case 7://embedded only - case 0xA4://embedded only - case 0xAB://embedded only - break; - default: - break; - } - switch (Unknown_42h) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: - case 0xA: - case 0xB: - case 0xC: - case 0xE: - case 0x10: - case 0x12: - case 0x14: - case 0x15: - case 0x18: - case 0x19: - case 0x20: - case 0x21: - case 0x24: - case 0x2A: - case 0x40: - case 0x55: - case 0x80://embedded only - break; - default: - break; - } - switch (Unknown_43h) - { - case 0x20://32 - case 0x28://40 - case 0x30://48 - case 0: - break; - default: - break; - } - + switch (Usage) + { + case TextureUsage.UNKNOWN:// = 0, + case TextureUsage.DEFAULT:// = 1, + case TextureUsage.TERRAIN:// = 2, + case TextureUsage.CLOUDDENSITY:// = 3, + case TextureUsage.CLOUDNORMAL:// = 4, + case TextureUsage.CABLE:// = 5, + case TextureUsage.FENCE:// = 6, + case TextureUsage.SCRIPT:// = 8, + case TextureUsage.WATERFLOW:// = 9, + case TextureUsage.WATERFOAM:// = 10, + case TextureUsage.WATERFOG:// = 11, + case TextureUsage.WATEROCEAN:// = 12, + case TextureUsage.FOAMOPACITY:// = 14, + case TextureUsage.DIFFUSEMIPSHARPEN:// = 16, + case TextureUsage.DIFFUSEDARK:// = 18, + case TextureUsage.DIFFUSEALPHAOPAQUE:// = 19, + case TextureUsage.DIFFUSE:// = 20, + case TextureUsage.DETAIL:// = 21, + case TextureUsage.NORMAL:// = 22, + case TextureUsage.SPECULAR:// = 23, + case TextureUsage.EMISSIVE:// = 24, + case TextureUsage.TINTPALETTE:// = 25, + case TextureUsage.SKIPPROCESSING:// = 26, + break; + case TextureUsage.ENVEFF:// = 7, //unused by V + case TextureUsage.WATER:// = 13, //unused by V + case TextureUsage.FOAM:// = 15, //unused by V + case TextureUsage.DIFFUSEDETAIL:// = 17, //unused by V + case TextureUsage.DONOTOPTIMIZE:// = 27, //unused by V + case TextureUsage.TEST:// = 28, //unused by V + case TextureUsage.COUNT:// = 29, //unused by V + break; + default: + break; + } + + var uf = UsageFlags; + if ((uf & TextureUsageFlags.EMBEDDEDSCRIPTRT) > 0) // .ydr embedded script_rt textures, only 3 uses + { } + if ((uf & TextureUsageFlags.UNK19) > 0) + { } + if ((uf & TextureUsageFlags.UNK20) > 0) + { } + if ((uf & TextureUsageFlags.UNK21) > 0) + { } + if ((uf & TextureUsageFlags.UNK24) == 0)//wtf isthis? only 0 on special resident(?) textures and some reused ones + { } } - - /// - /// Writes the data-block to a stream. - /// public override void Write(ResourceDataWriter writer, params object[] parameters) { base.Write(writer, parameters); @@ -598,10 +492,62 @@ namespace CodeWalker.GameFiles writer.Write(this.Unknown_88h); writer.Write(this.Unknown_8Ch); } + public override void WriteXml(StringBuilder sb, int indent, string ddsfolder) + { + base.WriteXml(sb, indent, ddsfolder); + YtdXml.ValueTag(sb, indent, "Width", Width.ToString()); + YtdXml.ValueTag(sb, indent, "Height", Height.ToString()); + YtdXml.ValueTag(sb, indent, "MipLevels", Levels.ToString()); + YtdXml.StringTag(sb, indent, "Format", Format.ToString()); + YtdXml.StringTag(sb, indent, "Usage", Usage.ToString()); + YtdXml.StringTag(sb, indent, "UsageFlags", UsageFlags.ToString()); + YtdXml.ValueTag(sb, indent, "ExtraFlags", ExtraFlags.ToString()); + YtdXml.StringTag(sb, indent, "FileName", (Name ?? "null") + ".dds"); + + try + { + var filepath = System.IO.Path.Combine(ddsfolder, (Name ?? "null") + ".dds"); + var dds = DDSIO.GetDDSFile(this); + System.IO.File.WriteAllBytes(filepath, dds); + } + catch { } + } + public override void ReadXml(XmlNode node, string ddsfolder) + { + base.ReadXml(node, ddsfolder); + Width = (ushort)Xml.GetChildUIntAttribute(node, "Width", "value"); + Height = (ushort)Xml.GetChildUIntAttribute(node, "Height", "value"); + Levels = (byte)Xml.GetChildUIntAttribute(node, "MipLevels", "value"); + Format = Xml.GetChildEnumInnerText(node, "Format"); + Usage = Xml.GetChildEnumInnerText(node, "Usage"); + UsageFlags = Xml.GetChildEnumInnerText(node, "UsageFlags"); + ExtraFlags = Xml.GetChildUIntAttribute(node, "ExtraFlags", "value"); + var filename = Xml.GetChildInnerText(node, "FileName"); + + try + { + var filepath = System.IO.Path.Combine(ddsfolder, filename); + if (System.IO.File.Exists(filepath)) + { + var dds = System.IO.File.ReadAllBytes(filepath); + var tex = DDSIO.GetTexture(dds); + if (tex != null) + { + Data = tex.Data; + Width = tex.Width; + Height = tex.Height; + Depth = tex.Depth; + Levels = tex.Levels; + Format = tex.Format; + Stride = tex.Stride; + } + } + } + catch { } + + + } - /// - /// Returns a list of data blocks which are referenced by this block. - /// public override IResourceBlock[] GetReferences() { var list = new List(base.GetReferences()); @@ -678,7 +624,6 @@ namespace CodeWalker.GameFiles //UNKNOWN } - public enum TextureUsage : byte { UNKNOWN = 0, @@ -717,29 +662,29 @@ namespace CodeWalker.GameFiles { NOT_HALF = 1, HD_SPLIT = (1 << 1), - UNK2 = (1 << 2), - UNK3 = (1 << 3), - UNK4 = (1 << 4), - UNK5 = (1 << 5), - UNK6 = (1 << 6), - UNK7 = (1 << 7), - UNK8 = (1 << 8), - UNK9 = (1 << 9), - UNK10 = (1 << 10), - UNK11 = (1 << 11), - UNK12 = (1 << 12), - UNK13 = (1 << 13), - UNK14 = (1 << 14), - UNK15 = (1 << 15), - UNK16 = (1 << 16), - UNK17 = (1 << 17), - UNK18 = (1 << 18), - UNK19 = (1 << 19), - UNK20 = (1 << 20), - UNK21 = (1 << 21), + X2 = (1 << 2), + X4 = (1 << 3), + Y4 = (1 << 4), + X8 = (1 << 5), + X16 = (1 << 6), + X32 = (1 << 7), + X64 = (1 << 8), + Y64 = (1 << 9), + X128 = (1 << 10), + X256 = (1 << 11), + X512 = (1 << 12), + Y512 = (1 << 13), + X1024 = (1 << 14),//wtf is all this? + Y1024 = (1 << 15), + X2048 = (1 << 16), + Y2048 = (1 << 17), + EMBEDDEDSCRIPTRT = (1 << 18), + UNK19 = (1 << 19), //unused by V + UNK20 = (1 << 20), //unused by V + UNK21 = (1 << 21), //unused by V FLAG_FULL = (1 << 22), MAPS_HALF = (1 << 23), - UNK24 = (1 << 24), + UNK24 = (1 << 24),//used by almost everything... } diff --git a/ExploreForm.cs b/ExploreForm.cs index 47ff2da..6e103bf 100644 --- a/ExploreForm.cs +++ b/ExploreForm.cs @@ -256,7 +256,7 @@ namespace CodeWalker InitFileType(".gif", "GIF Image", 16); InitFileType(".png", "Portable Network Graphics", 16); InitFileType(".dds", "DirectDraw Surface", 16); - InitFileType(".ytd", "Texture Dictionary", 16, FileTypeAction.ViewYtd); + InitFileType(".ytd", "Texture Dictionary", 16, FileTypeAction.ViewYtd, true); InitFileType(".mrf", "MRF File", 18); InitFileType(".ycd", "Clip Dictionary", 18, FileTypeAction.ViewYcd, true); InitFileType(".ypt", "Particle Effect", 18, FileTypeAction.ViewModel); @@ -1910,7 +1910,16 @@ namespace CodeWalker } private void ExportXml() { + bool isytd = false;//need a folder to output ytd XML to, for the texture .dds files if (MainListView.SelectedIndices.Count == 1) + { + var idx = MainListView.SelectedIndices[0]; + if ((idx < 0) || (idx >= CurrentFiles.Count)) return; + var file = CurrentFiles[idx]; + isytd = file?.File?.NameLower?.EndsWith(".ytd") == true; + } + + if ((MainListView.SelectedIndices.Count == 1) && (!isytd)) { var idx = MainListView.SelectedIndices[0]; if ((idx < 0) || (idx >= CurrentFiles.Count)) return; @@ -1975,7 +1984,7 @@ namespace CodeWalker } string newfn; - string xml = MetaXml.GetXml(file.File, data, out newfn); + string xml = MetaXml.GetXml(file.File, data, out newfn, folderpath); if (string.IsNullOrEmpty(xml)) { errors.AppendLine("Unable to convert file to XML: " + file.Path); @@ -2422,6 +2431,7 @@ namespace CodeWalker var fi = new FileInfo(fpath); var fname = fi.Name; var fnamel = fname.ToLowerInvariant(); + var fpathin = fpath; var mformat = MetaFormat.RSC; var trimlength = 4; @@ -2456,9 +2466,14 @@ namespace CodeWalker { mformat = MetaFormat.Ybn; } + if (fnamel.EndsWith(".ytd.xml")) + { + mformat = MetaFormat.Ytd; + } fname = fname.Substring(0, fname.Length - trimlength); fnamel = fnamel.Substring(0, fnamel.Length - trimlength); + fpathin = fpathin.Substring(0, fpathin.Length - trimlength); var doc = new XmlDocument(); string text = File.ReadAllText(fpath); @@ -2548,6 +2563,17 @@ namespace CodeWalker data = ybn.Save(); break; } + case MetaFormat.Ytd: + { + var ytd = XmlYtd.GetYtd(doc, fpathin); + if (ytd.TextureDict == null) + { + MessageBox.Show(fname + ": Schema not supported.", "Cannot import YTD XML"); + continue; + } + data = ytd.Save(); + break; + } }