XML/RBF conversion

This commit is contained in:
dexy 2019-12-30 05:23:09 +11:00
parent e31e401fa8
commit e3bbd29b33
6 changed files with 351 additions and 36 deletions

View File

@ -99,6 +99,7 @@
<Compile Include="GameFiles\MetaTypes\Rbf.cs" /> <Compile Include="GameFiles\MetaTypes\Rbf.cs" />
<Compile Include="GameFiles\MetaTypes\XmlMeta.cs" /> <Compile Include="GameFiles\MetaTypes\XmlMeta.cs" />
<Compile Include="GameFiles\MetaTypes\XmlPso.cs" /> <Compile Include="GameFiles\MetaTypes\XmlPso.cs" />
<Compile Include="GameFiles\MetaTypes\XmlRbf.cs" />
<Compile Include="GameFiles\Resources\Bounds.cs" /> <Compile Include="GameFiles\Resources\Bounds.cs" />
<Compile Include="GameFiles\Resources\Clip.cs" /> <Compile Include="GameFiles\Resources\Clip.cs" />
<Compile Include="GameFiles\Resources\Clothes.cs" /> <Compile Include="GameFiles\Resources\Clothes.cs" />

View File

@ -1517,9 +1517,37 @@ namespace CodeWalker.GameFiles
private static void WriteNode(StringBuilder sb, int indent, RbfStructure rs) private static void WriteNode(StringBuilder sb, int indent, RbfStructure rs)
{ {
var attStr = "";
if (rs.Attributes.Count > 0)
{
var asb = new StringBuilder();
foreach (var attr in rs.Attributes)
{
if (attr is RbfString str)
{
asb.Append($" {attr.Name}=\"{str.Value}\"");
}
else if (attr is RbfFloat flt)
{
asb.Append($" {attr.Name}=\"{FloatUtil.ToString(flt.Value)}\"");
}
else if (attr is RbfUint32 unt)
{
asb.Append($" {attr.Name}=\"{unt.Value.ToString()}\"");
}
else if (attr is RbfBoolean bln)
{
asb.Append($" {attr.Name}=\"{bln.Value.ToString()}\"");
}
else
{ }
}
attStr = $"{asb.ToString()}";
}
if (rs.Children.Count == 0) if (rs.Children.Count == 0)
{ {
SelfClosingTag(sb, indent, rs.Name); SelfClosingTag(sb, indent, rs.Name + attStr);
return; return;
} }
@ -1527,7 +1555,7 @@ namespace CodeWalker.GameFiles
bool oneline = ((rs.Children.Count == 1) && (rs.Children[0].Name == null)); bool oneline = ((rs.Children.Count == 1) && (rs.Children[0].Name == null));
OpenTag(sb, indent, rs.Name, !oneline); OpenTag(sb, indent, rs.Name + attStr, !oneline);
foreach (var child in rs.Children) foreach (var child in rs.Children)

View File

@ -42,6 +42,7 @@ namespace CodeWalker.GameFiles
public RbfStructure current { get; set; } public RbfStructure current { get; set; }
public Stack<RbfStructure> stack { get; set; } public Stack<RbfStructure> stack { get; set; }
public List<RbfEntryDescription> descriptors { get; set; } public List<RbfEntryDescription> descriptors { get; set; }
public Dictionary<string, int> outDescriptors { get; private set; } = new Dictionary<string, int>();
public RbfStructure Load(string fileName) public RbfStructure Load(string fileName)
{ {
@ -130,7 +131,7 @@ namespace CodeWalker.GameFiles
private void ParseElement(DataReader reader, int descriptorIndex, byte dataType) private void ParseElement(DataReader reader, int descriptorIndex, byte dataType)
{ {
var descriptor = descriptors[descriptorIndex]; var descriptor = descriptors[descriptorIndex];
switch (dataType) //(descriptor.Type) switch (dataType)
{ {
case 0: // open element... case 0: // open element...
{ {
@ -139,22 +140,15 @@ namespace CodeWalker.GameFiles
if (current != null) if (current != null)
{ {
current.Children.Add(structureValue); current.AddChild(structureValue);
stack.Push(current); stack.Push(current);
} }
current = structureValue; current = structureValue;
// 6 bytes
var x1 = reader.ReadInt16(); var x1 = reader.ReadInt16();
var x2 = reader.ReadInt16(); var x2 = reader.ReadInt16();
var x3 = reader.ReadInt16(); current.PendingAttributes = reader.ReadInt16();
//if (x1 != 0)
// throw new Exception("unexpected");
//if (x2 != 0)
// throw new Exception("unexpected");
//if (x3 != 0)
// throw new Exception("unexpected");
break; break;
} }
case 0x10: case 0x10:
@ -162,7 +156,7 @@ namespace CodeWalker.GameFiles
var intValue = new RbfUint32(); var intValue = new RbfUint32();
intValue.Name = descriptor.Name; intValue.Name = descriptor.Name;
intValue.Value = reader.ReadUInt32(); intValue.Value = reader.ReadUInt32();
current.Children.Add(intValue); current.AddChild(intValue);
break; break;
} }
case 0x20: case 0x20:
@ -170,7 +164,7 @@ namespace CodeWalker.GameFiles
var booleanValue = new RbfBoolean(); var booleanValue = new RbfBoolean();
booleanValue.Name = descriptor.Name; booleanValue.Name = descriptor.Name;
booleanValue.Value = true; booleanValue.Value = true;
current.Children.Add(booleanValue); current.AddChild(booleanValue);
break; break;
} }
case 0x30: case 0x30:
@ -178,7 +172,7 @@ namespace CodeWalker.GameFiles
var booleanValue = new RbfBoolean(); var booleanValue = new RbfBoolean();
booleanValue.Name = descriptor.Name; booleanValue.Name = descriptor.Name;
booleanValue.Value = false; booleanValue.Value = false;
current.Children.Add(booleanValue); current.AddChild(booleanValue);
break; break;
} }
case 0x40: case 0x40:
@ -186,7 +180,7 @@ namespace CodeWalker.GameFiles
var floatValue = new RbfFloat(); var floatValue = new RbfFloat();
floatValue.Name = descriptor.Name; floatValue.Name = descriptor.Name;
floatValue.Value = reader.ReadSingle(); floatValue.Value = reader.ReadSingle();
current.Children.Add(floatValue); current.AddChild(floatValue);
break; break;
} }
case 0x50: case 0x50:
@ -196,7 +190,7 @@ namespace CodeWalker.GameFiles
floatVectorValue.X = reader.ReadSingle(); floatVectorValue.X = reader.ReadSingle();
floatVectorValue.Y = reader.ReadSingle(); floatVectorValue.Y = reader.ReadSingle();
floatVectorValue.Z = reader.ReadSingle(); floatVectorValue.Z = reader.ReadSingle();
current.Children.Add(floatVectorValue); current.AddChild(floatVectorValue);
break; break;
} }
case 0x60: case 0x60:
@ -207,7 +201,7 @@ namespace CodeWalker.GameFiles
var stringValue = new RbfString(); var stringValue = new RbfString();
stringValue.Name = descriptor.Name; stringValue.Name = descriptor.Name;
stringValue.Value = value; stringValue.Value = value;
current.Children.Add(stringValue); current.AddChild(stringValue);
break; break;
} }
default: default:
@ -225,6 +219,66 @@ namespace CodeWalker.GameFiles
return isrbf; return isrbf;
} }
public byte GetDescriptorIndex(IRbfType t, out bool isNew)
{
var key = $"{t.Name}_{t.DataType}";
isNew = false;
if (!outDescriptors.TryGetValue(key, out var idx))
{
idx = outDescriptors.Count;
outDescriptors.Add(key, idx);
isNew = true;
}
return (byte)idx;
}
public byte[] Save()
{
var ms = new MemoryStream();
Save(ms);
var buf = new byte[ms.Length];
ms.Position = 0;
ms.Read(buf, 0, buf.Length);
return buf;
}
public void Save(string fileName)
{
using (var fileStream = new FileStream(fileName, FileMode.Create))
{
Save(fileStream);
}
}
public void Save(Stream stream)
{
outDescriptors = new Dictionary<string, int>();
var writer = new DataWriter(stream);
writer.Write(RBF_IDENT);
current.Save(this, writer);
}
public void WriteRecordId(IRbfType type, DataWriter writer)
{
writer.Write(GetDescriptorIndex(type, out var isNew));
writer.Write((byte)type.DataType);
if (isNew)
{
writer.Write((ushort)type.Name.Length);
writer.Write(Encoding.ASCII.GetBytes(type.Name));
}
}
} }
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfEntryDescription [TypeConverter(typeof(ExpandableObjectConverter))] public class RbfEntryDescription
@ -236,23 +290,56 @@ namespace CodeWalker.GameFiles
[TypeConverter(typeof(ExpandableObjectConverter))] public interface IRbfType [TypeConverter(typeof(ExpandableObjectConverter))] public interface IRbfType
{ {
string Name { get; set; } string Name { get; set; }
} byte DataType { get; }
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfBoolean : IRbfType void Save(RbfFile file, DataWriter writer);
{
public string Name { get; set; }
public bool Value { get; set; }
public override string ToString() { return Name + ": " + Value.ToString(); }
} }
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfBytes : IRbfType [TypeConverter(typeof(ExpandableObjectConverter))] public class RbfBytes : IRbfType
{ {
public string Name { get; set; } public string Name { get; set; }
public byte[] Value { get; set; } public byte[] Value { get; set; }
public byte DataType => 0;
public void Save(RbfFile root, DataWriter writer)
{
writer.Write((byte)0xFD);
writer.Write((byte)0xFF);
writer.Write(Value.Length);
writer.Write(Value);
}
public override string ToString() { return Name + ": " + Value.ToString(); }
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfUint32 : IRbfType
{
public string Name { get; set; }
public uint Value { get; set; }
public byte DataType => 0x10;
public void Save(RbfFile file, DataWriter writer)
{
file.WriteRecordId(this, writer);
writer.Write(Value);
}
public override string ToString() { return Name + ": " + Value.ToString(); }
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfBoolean : IRbfType
{
public string Name { get; set; }
public bool Value { get; set; }
public byte DataType => (byte)((Value) ? 0x20 : 0x30);
public void Save(RbfFile file, DataWriter writer)
{
file.WriteRecordId(this, writer);
}
public override string ToString() { return Name + ": " + Value.ToString(); } public override string ToString() { return Name + ": " + Value.ToString(); }
} }
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfFloat : IRbfType [TypeConverter(typeof(ExpandableObjectConverter))] public class RbfFloat : IRbfType
{ {
public string Name { get; set; } public string Name { get; set; }
public float Value { get; set; } public float Value { get; set; }
public byte DataType => 0x40;
public void Save(RbfFile file, DataWriter writer)
{
file.WriteRecordId(this, writer);
writer.Write(Value);
}
public override string ToString() { return Name + ": " + Value.ToString(); } public override string ToString() { return Name + ": " + Value.ToString(); }
} }
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfFloat3 : IRbfType [TypeConverter(typeof(ExpandableObjectConverter))] public class RbfFloat3 : IRbfType
@ -261,18 +348,36 @@ namespace CodeWalker.GameFiles
public float X { get; set; } public float X { get; set; }
public float Y { get; set; } public float Y { get; set; }
public float Z { get; set; } public float Z { get; set; }
public byte DataType => 0x50;
public void Save(RbfFile file, DataWriter writer)
{
file.WriteRecordId(this, writer);
writer.Write(X);
writer.Write(Y);
writer.Write(Z);
}
public override string ToString() { return string.Format("{0}: X:{1}, Y:{2}, Z:{3}", Name, X, Y, Z); } public override string ToString() { return string.Format("{0}: X:{1}, Y:{2}, Z:{3}", Name, X, Y, Z); }
} }
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfString : IRbfType [TypeConverter(typeof(ExpandableObjectConverter))] public class RbfString : IRbfType
{ {
public string Name { get; set; } public string Name { get; set; }
public string Value { get; set; } public string Value { get; set; }
public byte DataType => 0x60;
public void Save(RbfFile file, DataWriter writer)
{
file.WriteRecordId(this, writer);
writer.Write((short)Value.Length);
writer.Write(Encoding.ASCII.GetBytes(Value));
}
public override string ToString() { return Name + ": " + Value.ToString(); } public override string ToString() { return Name + ": " + Value.ToString(); }
} }
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfStructure : IRbfType [TypeConverter(typeof(ExpandableObjectConverter))] public class RbfStructure : IRbfType
{ {
public string Name { get; set; } public string Name { get; set; }
public List<IRbfType> Children { get; set; } = new List<IRbfType>(); public List<IRbfType> Children { get; set; } = new List<IRbfType>();
public List<IRbfType> Attributes { get; set; } = new List<IRbfType>();
internal int PendingAttributes { get; set; }
public byte DataType => 0;
public override string ToString() { return Name + ": {" + Children.Count.ToString() + "}"; } public override string ToString() { return Name + ": {" + Children.Count.ToString() + "}"; }
public IRbfType FindChild(string name) public IRbfType FindChild(string name)
{ {
@ -283,12 +388,39 @@ namespace CodeWalker.GameFiles
} }
return null; return null;
} }
} public void Save(RbfFile root, DataWriter writer)
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfUint32 : IRbfType {
{ root.WriteRecordId(this, writer);
public string Name { get; set; }
public uint Value { get; set; } writer.Write(new byte[4]); // 00
public override string ToString() { return Name + ": " + Value.ToString(); }
// count of non-primitive fields in this (... attributes??)
writer.Write((short)Attributes.Count); //writer.Write((short)Children.TakeWhile(a => !(a is RbfBytes || a is RbfStructure)).Count());
foreach (var attr in Attributes)
{
attr.Save(root, writer);
}
foreach (var child in Children)
{
child.Save(root, writer);
}
writer.Write((byte)0xFF);
writer.Write((byte)0xFF);
}
internal void AddChild(IRbfType value)
{
if (PendingAttributes > 0)
{
PendingAttributes--;
Attributes.Add(value);
}
else
{
Children.Add(value);
}
}
} }

View File

@ -0,0 +1,142 @@
using SharpDX;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace CodeWalker.GameFiles
{
public class XmlRbf
{
public static RbfFile GetRbf(XmlDocument doc)
{
var rbf = new RbfFile();
using (var reader = new XmlNodeReader(doc))
{
reader.MoveToContent();
rbf.current = (RbfStructure) Traverse(XDocument.Load(reader).Root);
}
return rbf;
}
private static IRbfType Traverse(XNode node)
{
if (node is XElement element)
{
if (element.Attribute("value") != null)
{
var val = element.Attribute("value").Value;
if (!string.IsNullOrEmpty(val))
{
var rval = CreateValueNode(element.Name.LocalName, val);
if (rval != null)
{
return rval;
}
}
}
else if ((element.Attributes().Count() == 3) && (element.Attribute("x") != null) && (element.Attribute("y") != null) && (element.Attribute("z") != null))
{
FloatUtil.TryParse(element.Attribute("x").Value, out float x);
FloatUtil.TryParse(element.Attribute("y").Value, out float y);
FloatUtil.TryParse(element.Attribute("z").Value, out float z);
return new RbfFloat3()
{
Name = element.Name.LocalName,
X = x,
Y = y,
Z = z
};
}
else if ((element.Elements().Count() == 0) && (element.Attributes().Count() == 0)) //else if (element.Name == "type" || element.Name == "key" || element.Name == "platform")
{
return new RbfString()
{
Name = element.Name.LocalName,
Value = element.Value
};
}
var n = new RbfStructure();
n.Name = element.Name.LocalName;
n.Children = element.Nodes().Select(c => Traverse(c)).ToList();
foreach (var attr in element.Attributes())
{
var val = attr.Value;
var aval = CreateValueNode(attr.Name.LocalName, val);
if (aval != null)
{
n.Attributes.Add(aval);
}
}
return n;
}
else if (node is XText text)
{
return new RbfBytes()
{
Name = "",
Value = Encoding.ASCII.GetBytes(text.Value).Concat(new byte[] { 0x00 }).ToArray()
};
}
return null;
}
private static IRbfType CreateValueNode(string name, string val)
{
if (val == "True")
{
return new RbfBoolean()
{
Name = name,
Value = true
};
}
else if (val == "False")
{
return new RbfBoolean()
{
Name = name,
Value = false
};
}
else if (val.StartsWith("0x"))
{
uint.TryParse(val.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint u);
return new RbfUint32()
{
Name = name,
Value = u
};
}
else if (FloatUtil.TryParse(val, out float f))
{
return new RbfFloat()
{
Name = name,
Value = f
};
}
else
{
return new RbfString()
{
Name = name,
Value = val
};
}
}
}
}

View File

@ -2504,7 +2504,13 @@ namespace CodeWalker
} }
case MetaFormat.RBF: case MetaFormat.RBF:
{ {
//todo! var rbf = XmlRbf.GetRbf(doc);
if (rbf.current == null)
{
MessageBox.Show(fname + ": Schema not supported.", "Cannot import RBF XML");
continue;
}
data = rbf.Save();
break; break;
} }
case MetaFormat.AudioRel: case MetaFormat.AudioRel:

View File

@ -373,11 +373,14 @@ namespace CodeWalker.Forms
data = pso.Save(); data = pso.Save();
break; break;
case MetaFormat.RBF: case MetaFormat.RBF:
MessageBox.Show("Sorry, RBF import is not supported.", "Cannot import RBF XML"); var rbf = XmlRbf.GetRbf(doc);
return false; if (rbf.current == null)
case MetaFormat.CacheFile: {
MessageBox.Show("Sorry, CacheFile import is not supported.", "Cannot import CacheFile XML"); MessageBox.Show("Schema not supported.", "Cannot import RBF XML");
return false; return false;
}
data = rbf.Save();
break;
case MetaFormat.Ynd: case MetaFormat.Ynd:
var ynd = XmlYnd.GetYnd(doc); var ynd = XmlYnd.GetYnd(doc);
if (ynd.NodeDictionary == null) if (ynd.NodeDictionary == null)
@ -387,6 +390,9 @@ namespace CodeWalker.Forms
} }
data = ynd.Save(); data = ynd.Save();
break; break;
case MetaFormat.CacheFile:
MessageBox.Show("Sorry, CacheFile import is not supported.", "Cannot import CacheFile XML");
return false;
} }
} }
#if !DEBUG #if !DEBUG