YTD/XML conversion

This commit is contained in:
dexy 2020-01-17 23:02:55 +11:00
parent 21a7c3e6e3
commit f81958679a
6 changed files with 327 additions and 279 deletions

View File

@ -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;
}
}
}

View File

@ -3440,7 +3440,7 @@ namespace CodeWalker.GameFiles
}
public void TestYtds()
{
bool ddstest = true;
bool ddstest = false;
bool savetest = false;
var errorfiles = new List<RpfEntry>();
foreach (RpfFile file in AllRpfs)

View File

@ -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<YbnFile>(e, data);
return GetXml(ybn, out filename);
}
else if (fnl.EndsWith(".ytd"))
{
YtdFile ytd = RpfFile.GetFile<YtdFile>(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,
}
}

View File

@ -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<uint> data_block;//used for saving.

View File

@ -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<uint, Texture> 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<Texture>();
}
/// <summary>
/// Reads the data-block from a stream.
/// </summary>
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<ResourceSimpleList64_uint>();
this.Textures = reader.ReadBlock<ResourcePointerList64<Texture>>();
var dict = new Dictionary<uint, Texture>();
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<uint, Texture>(dict);
BuildDict();
}
/// <summary>
/// Writes the data-block to a stream.
/// </summary>
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<Texture>();
var texturehashes = new List<uint>();
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<Texture>();
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<long, IResourceBlock>[] GetParts()
{
@ -118,20 +159,21 @@ namespace CodeWalker.GameFiles
return tex;
}
//public Dictionary<uint, Texture> GetDictionary()
//{
// Dictionary<uint, Texture> td = new Dictionary<uint, Texture>();
// 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<uint, Texture>();
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;
/// <summary>
/// Reads the data-block from a stream.
/// </summary>
public override void Read(ResourceDataReader reader, params object[] parameters)
{
// read structure data
@ -214,10 +253,6 @@ namespace CodeWalker.GameFiles
break;
}
}
/// <summary>
/// Writes the data-block to a stream.
/// </summary>
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");
}
/// <summary>
/// Returns a list of data blocks which are referenced by this block.
/// </summary>
public override IResourceBlock[] GetReferences()
{
var list = new List<IResourceBlock>();
@ -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
}
}
/// <summary>
/// Reads the data-block from a stream.
/// </summary>
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<TextureData>(
this.DataPointer, // offset
this.Format,
this.Width,
this.Height,
this.Levels,
this.Stride
);
this.Data = reader.ReadBlockAt<TextureData>(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
{ }
}
/// <summary>
/// Writes the data-block to a stream.
/// </summary>
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<TextureFormat>(node, "Format");
Usage = Xml.GetChildEnumInnerText<TextureUsage>(node, "Usage");
UsageFlags = Xml.GetChildEnumInnerText<TextureUsageFlags>(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 { }
}
/// <summary>
/// Returns a list of data blocks which are referenced by this block.
/// </summary>
public override IResourceBlock[] GetReferences()
{
var list = new List<IResourceBlock>(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...
}

View File

@ -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;
}
}