CodeWalker/GameFiles/MetaTypes/MetaXml.cs
2017-10-04 18:44:29 +11:00

1684 lines
73 KiB
C#

using SharpDX;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.GameFiles
{
public class MetaXml : MetaXmlBase
{
public static string GetXml(RpfFileEntry e, byte[] data, out string filename)
{
var fn = e.Name;
var fnl = fn.ToLowerInvariant();
if (fnl.EndsWith(".ymt"))
{
YmtFile ymt = RpfFile.GetFile<YmtFile>(e, data);
return GetXml(ymt, out filename);
}
else if (fnl.EndsWith(".ymf"))
{
YmfFile ymf = RpfFile.GetFile<YmfFile>(e, data);
return GetXml(ymf, out filename);
}
else if (fnl.EndsWith(".ymap"))
{
YmapFile ymap = RpfFile.GetFile<YmapFile>(e, data);
return GetXml(ymap, out filename);
}
else if (fnl.EndsWith(".ytyp"))
{
YtypFile ytyp = RpfFile.GetFile<YtypFile>(e, data);
return GetXml(ytyp, out filename);
}
else if (fnl.EndsWith(".pso"))
{
JPsoFile pso = RpfFile.GetFile<JPsoFile>(e, data);
return GetXml(pso, out filename);
}
else if (fnl.EndsWith(".cut"))
{
CutFile cut = RpfFile.GetFile<CutFile>(e, data);
return GetXml(cut, out filename);
}
filename = fn;
return string.Empty;
}
public static string GetXml(YmtFile ymt, out string filename)
{
var fn = (ymt?.RpfFileEntry?.Name) ?? "";
if (ymt.Meta != null) { filename = fn + ".xml"; return GetXml(ymt.Meta); }
else if (ymt.Pso != null) { filename = fn + ".pso.xml"; return PsoXml.GetXml(ymt.Pso); }
else if (ymt.Rbf != null) { filename = fn + ".rbf.xml"; return RbfXml.GetXml(ymt.Rbf); }
filename = string.Empty;
return string.Empty;
}
public static string GetXml(YmfFile ymf, out string filename)
{
var fn = (ymf?.FileEntry?.Name) ?? "";
if (ymf.Meta != null) { filename = fn + ".xml"; return GetXml(ymf.Meta); }
else if (ymf.Pso != null) { filename = fn + ".pso.xml"; return PsoXml.GetXml(ymf.Pso); }
else if (ymf.Rbf != null) { filename = fn + ".rbf.xml"; return RbfXml.GetXml(ymf.Rbf); }
filename = string.Empty;
return string.Empty;
}
public static string GetXml(YmapFile ymap, out string filename)
{
var fn = (ymap?.RpfFileEntry?.Name) ?? "";
if (ymap.Meta != null) { filename = fn + ".xml"; return GetXml(ymap.Meta); }
else if (ymap.Pso != null) { filename = fn + ".pso.xml"; return PsoXml.GetXml(ymap.Pso); }
else if (ymap.Rbf != null) { filename = fn + ".rbf.xml"; return RbfXml.GetXml(ymap.Rbf); }
filename = string.Empty;
return string.Empty;
}
public static string GetXml(YtypFile ytyp, out string filename)
{
var fn = (ytyp?.FileEntry?.Name) ?? "";
if (ytyp.Meta != null) { filename = fn + ".xml"; return GetXml(ytyp.Meta); }
else if (ytyp.Pso != null) { filename = fn + ".pso.xml"; return PsoXml.GetXml(ytyp.Pso); }
else if (ytyp.Rbf != null) { filename = fn + ".rbf.xml"; return RbfXml.GetXml(ytyp.Rbf); }
filename = string.Empty;
return string.Empty;
}
public static string GetXml(JPsoFile pso, out string filename)
{
var fn = (pso?.FileEntry?.Name) ?? "";
if (pso.Pso != null) { filename = fn + ".pso.xml"; return PsoXml.GetXml(pso.Pso); }
filename = string.Empty;
return string.Empty;
}
public static string GetXml(CutFile cut, out string filename)
{
var fn = (cut?.FileEntry?.Name) ?? "";
if (cut.Pso != null) { filename = fn + ".pso.xml"; return PsoXml.GetXml(cut.Pso); }
filename = string.Empty;
return string.Empty;
}
public static string GetXml(Meta meta)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
if (meta != null)
{
var cont = new MetaCont(meta);
WriteNode(sb, 0, cont, meta.RootBlockIndex, 0, XmlTagMode.Structure);
}
return sb.ToString();
}
private static void WriteNode(StringBuilder sb, int indent, MetaCont cont, int blockId, int offset, XmlTagMode tagMode = XmlTagMode.None, MetaName structName = 0)
{
var block = cont.Meta.GetBlock(blockId);
if (block == null)
{
ErrorXml(sb, indent, "Couldn't find block " + blockId + "!");
return;
}
if (structName == 0)
{
structName = block.StructureNameHash;
}
var name = HashString(structName);
var data = block.Data;
var structInfo = cont.GetStructureInfo(structName);
if (structInfo == null)
{
ErrorXml(sb, indent, "Couldn't find structure info " + name + "!");
return;
}
if (structInfo.Entries == null)
{
ErrorXml(sb, indent, "Couldn't find structure info entries for " + name + "!");
return;
}
switch (tagMode)
{
case XmlTagMode.Structure:
OpenTag(sb, indent, name);
break;
case XmlTagMode.Item:
OpenTag(sb, indent, "Item");
break;
case XmlTagMode.ItemAndType:
OpenTag(sb, indent, "Item type=\"" + name + "\"");
break;
}
var cind = indent + 1;
MetaStructureEntryInfo_s arrEntry = new MetaStructureEntryInfo_s();
for (int i = 0; i < structInfo.Entries.Length; i++)
{
var entry = structInfo.Entries[i];
if (entry.EntryNameHash == MetaName.ARRAYINFO)
{
arrEntry = entry;
continue;
}
var ename = HashString(entry.EntryNameHash);
var eoffset = offset + entry.DataOffset;
switch (entry.DataType)
{
default:
ErrorXml(sb, cind, ename + ": Unexpected entry DataType: " + entry.DataType.ToString());
break;
case MetaStructureEntryDataType.Array:
WriteArrayNode(sb, cind, cont, data, arrEntry, ename, eoffset);
break;
case MetaStructureEntryDataType.ArrayOfBytes:
OpenTag(sb, cind, ename, false);
var byteArrLen = (int)entry.ReferenceKey;
for (int n = 0; n < byteArrLen; n++)
{
var bidx = eoffset + n;
byte b = ((bidx >= 0) && (bidx < data.Length)) ? data[bidx] : (byte)0;
sb.Append(b.ToString("X").PadLeft(2, '0'));
}
CloseTag(sb, 0, ename);
break;
case MetaStructureEntryDataType.ArrayOfChars:
OpenTag(sb, cind, ename, false);
uint charArrLen = (uint)entry.ReferenceKey;
for (int n = 0; n < charArrLen; n++)
{
var bidx = eoffset + n;
if ((bidx >= 0) && (bidx < data.Length))
{
byte b = data[bidx];
if (b == 0) break;
sb.Append((char)b);
}
}
CloseTag(sb, 0, ename);
break;
case MetaStructureEntryDataType.Boolean:
var boolVal = BitConverter.ToBoolean(data, eoffset);
ValueTag(sb, cind, ename, boolVal?"true":"false");
break;
case MetaStructureEntryDataType.ByteEnum:
var byteEnumVal = data[eoffset];
ValueTag(sb, cind, ename, byteEnumVal.ToString());
break;
case MetaStructureEntryDataType.CharPointer:
var charPtr = MetaTypes.ConvertData<CharPointer>(data, eoffset);
string charStr = MetaTypes.GetString(cont.Meta, charPtr);
OneLineTag(sb, cind, ename, charStr);
break;
case MetaStructureEntryDataType.DataBlockPointer:
OpenTag(sb, cind, ename);
var dataPtr = MetaTypes.ConvertData<DataBlockPointer>(data, eoffset);
ErrorXml(sb, cind + 1, "DataBlockPointer not currently supported here!"); //TODO! ymap occludeModels vertices data is this type!
CloseTag(sb, cind, ename);
break;
case MetaStructureEntryDataType.Float:
var floatVal = BitConverter.ToSingle(data, eoffset);
ValueTag(sb, cind, ename, FloatUtil.ToString(floatVal));
break;
case MetaStructureEntryDataType.Float_XYZ:
var v3 = MetaTypes.ConvertData<Vector3>(data, eoffset);
SelfClosingTag(sb, cind, ename + " x=\"" + FloatUtil.ToString(v3.X) + "\" y=\"" + FloatUtil.ToString(v3.Y) + "\" z=\"" + FloatUtil.ToString(v3.Z) + "\"");
break;
case MetaStructureEntryDataType.Float_XYZW:
var v4 = MetaTypes.ConvertData<Vector4>(data, eoffset);
SelfClosingTag(sb, cind, ename + " x=\"" + FloatUtil.ToString(v4.X) + "\" y=\"" + FloatUtil.ToString(v4.Y) + "\" z=\"" + FloatUtil.ToString(v4.Z) + "\" w=\"" + FloatUtil.ToString(v4.W) + "\"");
break;
case MetaStructureEntryDataType.Hash:
var hashVal = MetaTypes.ConvertData<MetaHash>(data, eoffset);
var hashStr = HashString(hashVal);
StringTag(sb, cind, ename, hashStr);
break;
case MetaStructureEntryDataType.IntEnum:
var intEnumVal = BitConverter.ToInt32(data, eoffset);
var intEnumStr = GetEnumString(cont, entry, intEnumVal);
StringTag(sb, cind, ename, intEnumStr);
break;
case MetaStructureEntryDataType.IntFlags1:
var intFlags1Val = BitConverter.ToInt32(data, eoffset);
var intFlags1Str = GetEnumString(cont, entry, intFlags1Val);
StringTag(sb, cind, ename, intFlags1Str);
break;
case MetaStructureEntryDataType.IntFlags2:
var intFlags2Val = BitConverter.ToInt32(data, eoffset);
var intFlags2Str = GetEnumString(cont, entry, intFlags2Val);
StringTag(sb, cind, ename, intFlags2Str);
break;
case MetaStructureEntryDataType.ShortFlags:
var shortFlagsVal = BitConverter.ToInt16(data, eoffset);
var shortFlagsStr = GetEnumString(cont, entry, shortFlagsVal);
StringTag(sb, cind, ename, shortFlagsStr);
break;
case MetaStructureEntryDataType.SignedByte:
sbyte sbyteVal = (sbyte)data[eoffset];
ValueTag(sb, cind, ename, sbyteVal.ToString());
break;
case MetaStructureEntryDataType.SignedInt:
var intVal = BitConverter.ToInt32(data, eoffset);
ValueTag(sb, cind, ename, intVal.ToString());
break;
case MetaStructureEntryDataType.SignedShort:
var shortVal = BitConverter.ToInt16(data, eoffset);
ValueTag(sb, cind, ename, shortVal.ToString());
break;
case MetaStructureEntryDataType.Structure:
OpenTag(sb, cind, ename);
WriteNode(sb, cind, cont, blockId, eoffset, XmlTagMode.None, entry.ReferenceKey);
CloseTag(sb, cind, ename);
break;
case MetaStructureEntryDataType.StructurePointer:
OpenTag(sb, cind, ename);
ErrorXml(sb, cind + 1, "StructurePointer not supported here! Tell dexy!");
CloseTag(sb, cind, ename);
break;
case MetaStructureEntryDataType.UnsignedByte:
var byteVal = data[eoffset];
ValueTag(sb, cind, ename, byteVal.ToString());
//ValueTag(sb, cind, ename, "0x" + byteVal.ToString("X").PadLeft(2, '0'));
break;
case MetaStructureEntryDataType.UnsignedInt:
var uintVal = BitConverter.ToUInt32(data, eoffset);
switch (entry.EntryNameHash)
{
default:
ValueTag(sb, cind, ename, uintVal.ToString());
break;
case MetaName.color:
ValueTag(sb, cind, ename, "0x" + uintVal.ToString("X").PadLeft(8, '0'));
break;
}
break;
case MetaStructureEntryDataType.UnsignedShort:
var ushortVal = BitConverter.ToUInt16(data, eoffset);
ValueTag(sb, cind, ename, ushortVal.ToString());// "0x" + ushortVal.ToString("X").PadLeft(4, '0'));
break;
}
}
switch (tagMode)
{
case XmlTagMode.Structure:
CloseTag(sb, indent, name);
break;
case XmlTagMode.Item:
case XmlTagMode.ItemAndType:
CloseTag(sb, indent, "Item");
break;
}
}
private static void WriteArrayNode(StringBuilder sb, int indent, MetaCont cont, byte[] data, MetaStructureEntryInfo_s arrEntry, string ename, int eoffset)
{
int aCount = 0;
var aind = indent + 1;
string arrTag = ename;
switch (arrEntry.DataType)
{
default:
ErrorXml(sb, indent, ename + ": Unexpected array entry DataType: " + arrEntry.DataType.ToString());
break;
case MetaStructureEntryDataType.Structure:
var arrStruc = MetaTypes.ConvertData<Array_Structure>(data, eoffset);
var aBlockId = (int)arrStruc.PointerDataId;
var aOffset = (int)arrStruc.PointerDataOffset;
aCount = arrStruc.Count1;
arrTag += " itemType=\"" + HashString(arrEntry.ReferenceKey) + "\"";
if (aCount > 0)
{
OpenTag(sb, indent, arrTag);
var atyp = cont.GetStructureInfo(arrEntry.ReferenceKey);
var aBlock = cont.Meta.GetBlock(aBlockId);
for (int n = 0; n < aCount; n++)
{
WriteNode(sb, aind, cont, aBlockId, aOffset, XmlTagMode.Item, arrEntry.ReferenceKey);
aOffset += atyp.StructureSize;
if ((n < (aCount - 1)) && (aBlock != null) && (aOffset >= aBlock.DataLength))
{
aOffset = 0;
aBlockId++;
aBlock = cont.Meta.GetBlock(aBlockId);
}
}
CloseTag(sb, indent, ename);
}
else
{
SelfClosingTag(sb, indent, arrTag);
}
break;
case MetaStructureEntryDataType.StructurePointer:
var arrStrucP = MetaTypes.ConvertData<Array_StructurePointer>(data, eoffset);
var ptrArr = MetaTypes.GetPointerArray(cont.Meta, arrStrucP);
aCount = ptrArr?.Length ?? 0;
if (aCount > 0)
{
OpenTag(sb, indent, arrTag);
for (int n = 0; n < aCount; n++)
{
var ptr = ptrArr[n];
var offset = ptr.Offset;
WriteNode(sb, aind, cont, ptr.BlockID, offset, XmlTagMode.ItemAndType);
}
CloseTag(sb, indent, ename);
}
else
{
SelfClosingTag(sb, indent, arrTag);
}
break;
case MetaStructureEntryDataType.UnsignedInt:
var arrUint = MetaTypes.ConvertData<Array_uint>(data, eoffset);
var uintArr = MetaTypes.GetUintArray(cont.Meta, arrUint);
WriteRawArray(sb, uintArr, indent, ename, "uint");
break;
case MetaStructureEntryDataType.UnsignedShort:
var arrUshort = MetaTypes.ConvertData<Array_ushort>(data, eoffset);
var ushortArr = MetaTypes.GetUshortArray(cont.Meta, arrUshort);
WriteRawArray(sb, ushortArr, indent, ename, "ushort");
break;
case MetaStructureEntryDataType.UnsignedByte:
var arrUbyte = MetaTypes.ConvertData<Array_byte>(data, eoffset);
var byteArr = MetaTypes.GetByteArray(cont.Meta, arrUbyte);
WriteRawArray(sb, byteArr, indent, ename, "byte");
break;
case MetaStructureEntryDataType.Float:
var arrFloat = MetaTypes.ConvertData<Array_float>(data, eoffset);
var floatArr = MetaTypes.GetFloatArray(cont.Meta, arrFloat);
WriteRawArray(sb, floatArr, indent, ename, "float");
break;
case MetaStructureEntryDataType.Float_XYZ:
var arrV3 = MetaTypes.ConvertData<Array_Vector3>(data, eoffset);
var v4Arr = MetaTypes.ConvertDataArray<Vector4>(cont.Meta, MetaName.VECTOR4, arrV3.Pointer, arrV3.Count1);
WriteItemArray(sb, v4Arr, indent, ename, "Vector3/4", FormatVector4);
break;
case MetaStructureEntryDataType.CharPointer:
ErrorXml(sb, indent, "CharPointer ARRAY not supported here! Tell dexy!");
break;
case MetaStructureEntryDataType.DataBlockPointer:
ErrorXml(sb, indent, "DataBlockPointer ARRAY not supported here! Tell dexy!");
break;
case MetaStructureEntryDataType.Hash:
var arrHash = MetaTypes.ConvertData<Array_uint>(data, eoffset);
var hashArr = MetaTypes.GetHashArray(cont.Meta, arrHash);
WriteItemArray(sb, hashArr, indent, ename, "Hash", FormatHash);
break;
}
}
private static string GetEnumString(MetaCont cont, MetaStructureEntryInfo_s entry, int value)
{
var eName = entry.ReferenceKey;
var eInfo = cont.GetEnumInfo(eName);
if ((eInfo == null) || (eInfo.Entries == null))
{
return value.ToString();
}
bool isFlags = (entry.DataType == MetaStructureEntryDataType.IntFlags1) ||
(entry.DataType == MetaStructureEntryDataType.IntFlags2);// ||
//(entry.DataType == MetaStructureEntryDataType.ShortFlags);
if (isFlags)
{
StringBuilder sb = new StringBuilder();
foreach (var ev in eInfo.Entries)
{
var v = ev.EntryValue;
var m = 1 << v;
if ((value & m) > 0)
{
if (sb.Length > 0) sb.Append(", ");
sb.Append(HashString(ev.EntryNameHash));
}
}
return sb.ToString();
}
else
{
foreach (var ev in eInfo.Entries)
{
if (ev.EntryValue == value)
{
return HashString(ev.EntryNameHash);
}
}
return value.ToString(); //if we got here, there was no match...
}
}
private class MetaCont
{
public Meta Meta { get; set; }
Dictionary<MetaName, MetaStructureInfo> structInfos = new Dictionary<MetaName, MetaStructureInfo>();
Dictionary<MetaName, MetaEnumInfo> enumInfos = new Dictionary<MetaName, MetaEnumInfo>();
public MetaCont(Meta meta)
{
Meta = meta;
if (meta.StructureInfos != null)
{
foreach (var si in meta.StructureInfos)
{
structInfos[si.StructureNameHash] = si;
}
}
if (meta.EnumInfos != null)
{
foreach (var ei in meta.EnumInfos)
{
enumInfos[ei.EnumNameHash] = ei;
}
}
}
public MetaStructureInfo GetStructureInfo(MetaName name)
{
MetaStructureInfo i = null;
structInfos.TryGetValue(name, out i);
return i;
}
public MetaEnumInfo GetEnumInfo(MetaName name)
{
MetaEnumInfo i = null;
enumInfos.TryGetValue(name, out i);
return i;
}
}
}
public class PsoXml : MetaXmlBase
{
public static string GetXml(PsoFile pso)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
if ((pso != null) && (pso.DataSection != null) && (pso.DataMapSection != null))
{
var cont = new PsoCont(pso);
WriteNode(sb, 0, cont, pso.DataMapSection.RootId, 0, XmlTagMode.Structure);
}
return sb.ToString();
}
private static void WriteNode(StringBuilder sb, int indent, PsoCont cont, int blockId, int offset, XmlTagMode tagMode = XmlTagMode.None, MetaName structName = 0)
{
var block = cont.Pso.GetBlock(blockId);
if (block == null)
{
ErrorXml(sb, indent, "Couldn't find block " + blockId + "!");
return;
}
var boffset = offset + block.Offset;
if (structName == 0)
{
structName = block.NameHash;
}
var name = HashString(structName);
var data = cont.Pso.DataSection.Data;
var structInfo = cont.GetStructureInfo(structName);
if (structInfo == null)
{
ErrorXml(sb, indent, "Couldn't find structure info " + name + "!");
return;
}
if (structInfo.Entries == null)
{
ErrorXml(sb, indent, "Couldn't find structure info entries for " + name + "!");
return;
}
switch (tagMode)
{
case XmlTagMode.Structure:
OpenTag(sb, indent, name);
break;
case XmlTagMode.Item:
OpenTag(sb, indent, "Item");
break;
case XmlTagMode.ItemAndType:
OpenTag(sb, indent, "Item type=\"" + name + "\"");
break;
}
var cind = indent + 1;
for (int i = 0; i < structInfo.Entries.Length; i++)
{
var entry = structInfo.Entries[i];
if (entry.EntryNameHash == MetaName.ARRAYINFO)
{
continue;
}
var ename = HashString(entry.EntryNameHash);
var eoffset = boffset + entry.DataOffset;
switch (entry.Type)
{
default:
ErrorXml(sb, cind, ename + ": Unexpected entry DataType: " + entry.Type.ToString());
break;
case PsoDataType.Array:
WriteArrayNode(sb, cind, cont, blockId, offset, entry, structInfo, ename);
break;
case PsoDataType.Bool:
var boolVal = BitConverter.ToBoolean(data, eoffset);
ValueTag(sb, cind, ename, boolVal?"true":"false");
break;
case PsoDataType.SByte: //was LONG_01h //signed byte?
//var long1Val = MetaTypes.SwapBytes(BitConverter.ToUInt64(data, eoffset));
//ValueTag(sb, cind, ename, long1Val.ToString());
var byte1Val = (sbyte)data[eoffset];
ValueTag(sb, cind, ename, byte1Val.ToString());
break;
case PsoDataType.UByte:
var byte2Val = data[eoffset];
ValueTag(sb, cind, ename, byte2Val.ToString());
break;
case PsoDataType.Enum:
var enumInfo = cont.GetEnumInfo((MetaName)entry.ReferenceKey);
switch (entry.Unk_5h)
{
default:
ErrorXml(sb, cind, ename + ": Unexpected Enum subtype: " + entry.Unk_5h.ToString());
break;
case 0: //int enum
var intEVal = MetaTypes.SwapBytes(BitConverter.ToInt32(data, eoffset));
var intE = enumInfo.FindEntry(intEVal);
StringTag(sb, cind, ename, HashString(intE?.EntryNameHash ?? 0));
break;
case 2: //byte enum
var byteEVal = data[eoffset];
var byteE = enumInfo.FindEntry(byteEVal);
StringTag(sb, cind, ename, HashString(byteE?.EntryNameHash ?? 0));
break;
}
break;
case PsoDataType.Flags:
uint fCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
uint fEntry = (entry.ReferenceKey & 0xFFFF);
var fEnt = structInfo.GetEntry((int)fEntry);
PsoEnumInfo flagsInfo = null;
if ((fEnt != null) && (fEnt.EntryNameHash == MetaName.ARRAYINFO))
{
flagsInfo = cont.GetEnumInfo((MetaName)fEnt.ReferenceKey);
}
if (flagsInfo == null)
{
flagsInfo = cont.GetEnumInfo(entry.EntryNameHash);
}
uint? flagsVal = null;
switch (entry.Unk_5h)
{
default:
ErrorXml(sb, cind, ename + ": Unexpected Flags subtype: " + entry.Unk_5h.ToString());
break;
case 0: //int flags
flagsVal = MetaTypes.SwapBytes(BitConverter.ToUInt32(data, eoffset));
break;
case 1: //short flags
flagsVal = MetaTypes.SwapBytes(BitConverter.ToUInt16(data, eoffset));
break;
case 2: //byte flags
flagsVal = data[eoffset];
break;
}
if (flagsVal.HasValue)
{
uint fv = flagsVal.Value;
if (flagsInfo != null)
{
string fstr = "";
for (int n = 0; n < flagsInfo.EntriesCount; n++)
{
var fentry = flagsInfo.Entries[n];
var fmask = (1 << fentry.EntryKey);
if ((fv & fmask) > 0)
{
if (fstr != "") fstr += " ";
fstr += HashString(fentry.EntryNameHash);
}
}
StringTag(sb, cind, ename, fstr);
}
else
{
if (fv != 0) ValueTag(sb, cind, ename, fv.ToString());
else SelfClosingTag(sb, cind, ename);
}
}
break;
case PsoDataType.Float:
var floatVal = MetaTypes.SwapBytes(BitConverter.ToSingle(data, eoffset));
ValueTag(sb, cind, ename, FloatUtil.ToString(floatVal));
break;
case PsoDataType.Float2:
var v2 = MetaTypes.SwapBytes(MetaTypes.ConvertData<Vector2>(data, eoffset));
SelfClosingTag(sb, cind, ename + " x=\"" + FloatUtil.ToString(v2.X) + "\" y=\"" + FloatUtil.ToString(v2.Y) + "\"");
break;
case PsoDataType.Float3:
var v3 = MetaTypes.SwapBytes(MetaTypes.ConvertData<Vector3>(data, eoffset));
SelfClosingTag(sb, cind, ename + " x=\"" + FloatUtil.ToString(v3.X) + "\" y=\"" + FloatUtil.ToString(v3.Y) + "\" z=\"" + FloatUtil.ToString(v3.Z) + "\"");
break;
case PsoDataType.Float3a: //TODO: check this!
var v3a = MetaTypes.SwapBytes(MetaTypes.ConvertData<Vector3>(data, eoffset));
SelfClosingTag(sb, cind, ename + " x=\"" + FloatUtil.ToString(v3a.X) + "\" y=\"" + FloatUtil.ToString(v3a.Y) + "\" z=\"" + FloatUtil.ToString(v3a.Z) + "\"");
break;
case PsoDataType.Float4a: //TODO: check this! //...why are there 3 different types of float3?
var v3b = MetaTypes.SwapBytes(MetaTypes.ConvertData<Vector3>(data, eoffset));
SelfClosingTag(sb, cind, ename + " x=\"" + FloatUtil.ToString(v3b.X) + "\" y=\"" + FloatUtil.ToString(v3b.Y) + "\" z=\"" + FloatUtil.ToString(v3b.Z) + "\"");
break;
case PsoDataType.Float4:
var v4 = MetaTypes.SwapBytes(MetaTypes.ConvertData<Vector4>(data, eoffset));
SelfClosingTag(sb, cind, ename + " x=\"" + FloatUtil.ToString(v4.X) + "\" y=\"" + FloatUtil.ToString(v4.Y) + "\" z=\"" + FloatUtil.ToString(v4.Z) + "\" w=\"" + FloatUtil.ToString(v4.W) + "\"");
break;
case PsoDataType.SInt: //TODO: convert hashes?
var int5Val = MetaTypes.SwapBytes(BitConverter.ToInt32(data, eoffset));
ValueTag(sb, cind, ename, int5Val.ToString());
break;
case PsoDataType.UInt:
switch (entry.Unk_5h)
{
default:
ErrorXml(sb, cind, ename + ": Unexpected Integer subtype: " + entry.Unk_5h.ToString());
break;
case 0: //signed int
var int6aVal = MetaTypes.SwapBytes(BitConverter.ToInt32(data, eoffset));
ValueTag(sb, cind, ename, int6aVal.ToString());
break;
case 1: //unsigned int
var int6bVal = MetaTypes.SwapBytes(BitConverter.ToUInt32(data, eoffset));
ValueTag(sb, cind, ename, "0x" + int6bVal.ToString("X").PadLeft(8, '0'));
break;
}
break;
case PsoDataType.Long:
var long2Val = MetaTypes.SwapBytes(BitConverter.ToUInt64(data, eoffset));
ValueTag(sb, cind, ename, long2Val.ToString());
break;
case PsoDataType.Map:
WriteMapNode(sb, indent, cont, eoffset, entry, structInfo, ename);
break;
case PsoDataType.SShort:
var short3Val = (short)MetaTypes.SwapBytes(BitConverter.ToUInt16(data, eoffset));
ValueTag(sb, cind, ename, short3Val.ToString());
break;
case PsoDataType.UShort:
var short4Val = MetaTypes.SwapBytes(BitConverter.ToUInt16(data, eoffset));
ValueTag(sb, cind, ename, short4Val.ToString());
break;
case PsoDataType.HFloat://half float?
var short1EVal = MetaTypes.SwapBytes(BitConverter.ToUInt16(data, eoffset));
ValueTag(sb, cind, ename, short1EVal.ToString());
break;
case PsoDataType.String:
var str0 = GetStringValue(cont.Pso, entry, data, eoffset);
if (str0 == null)
{
ErrorXml(sb, cind, ename + ": Unexpected String subtype: " + entry.Unk_5h.ToString());
}
else
{
StringTag(sb, cind, ename, str0);
}
break;
case PsoDataType.Structure:
switch (entry.Unk_5h)
{
default:
ErrorXml(sb, cind, ename + ": Unexpected Structure subtype: " + entry.Unk_5h.ToString());
break;
case 0: //default structure
OpenTag(sb, cind, ename);
WriteNode(sb, cind, cont, blockId, offset + entry.DataOffset, XmlTagMode.None, (MetaName)entry.ReferenceKey);
CloseTag(sb, cind, ename);
break;
case 3: //structure pointer...
case 4: //also pointer? what's the difference?
var ptrVal = MetaTypes.ConvertData<PsoPOINTER>(data, eoffset);
ptrVal.SwapEnd();
var pbid = ptrVal.BlockID;
bool pbok = true;
if (pbid <= 0)
{
pbok = false; //no block specified?
}
if (pbid > cont.Pso.DataMapSection.EntriesCount)
{
pbok = false; //bad pointer? different type..? should output an error message here?
}
if (pbok)
{
WriteNode(sb, cind, cont, ptrVal.BlockID, (int)ptrVal.ItemOffset, XmlTagMode.None, (MetaName)entry.ReferenceKey);
}
else
{
SelfClosingTag(sb, cind, ename);
}
break;
}
break;
}
}
switch (tagMode)
{
case XmlTagMode.Structure:
CloseTag(sb, indent, name);
break;
case XmlTagMode.Item:
case XmlTagMode.ItemAndType:
CloseTag(sb, indent, "Item");
break;
}
}
private static void WriteArrayNode(StringBuilder sb, int indent, PsoCont cont, int blockId, int offset, PsoStructureEntryInfo entry, PsoStructureInfo estruct, string ename)
{
var block = cont.Pso.GetBlock(blockId);
var boffset = offset + block.Offset;
var eoffset = boffset + entry.DataOffset;
var aOffset = offset + entry.DataOffset;
var aBlockId = blockId;
uint aCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
var aind = indent + 1;
string arrTag = ename;
PsoStructureEntryInfo arrEntry = estruct.GetEntry((int)(entry.ReferenceKey & 0xFFFF));
if (arrEntry == null)
{
ErrorXml(sb, indent, "ARRAYINFO not found for " + ename + "!");
return;
}
var data = cont.Pso.DataSection.Data;
switch (entry.Unk_5h)
{
default:
ErrorXml(sb, indent, ename + ": WIP! Unsupported Array subtype: " + entry.Unk_5h.ToString());
break;
case 0: //Array_Structure
var arrStruc = MetaTypes.ConvertData<Array_Structure>(data, eoffset);
arrStruc.SwapEnd();
aBlockId = (int)arrStruc.PointerDataId;
aOffset = (int)arrStruc.PointerDataOffset;
aCount = arrStruc.Count1;
break;
case 1: //Raw in-line array
break;
case 2: //also raw in-line array, but how different from above?
break;
case 4: //pointer array? default array?
if (arrEntry.Unk_5h == 3) //pointers...
{
var arrStruc4 = MetaTypes.ConvertData<Array_Structure>(data, eoffset);
arrStruc4.SwapEnd();
aBlockId = (int)arrStruc4.PointerDataId;
aOffset = (int)arrStruc4.PointerDataOffset;
aCount = arrStruc4.Count1;
}
break;
case 129: //also raw inline array? in junctions.pso
break;
}
switch (arrEntry.Type)
{
default:
ErrorXml(sb, indent, ename + ": WIP! Unsupported array entry DataType: " + arrEntry.Type.ToString());
break;
case PsoDataType.Array:
var rk0 = (entry.ReferenceKey >> 16) & 0x0000FFFF;
if (rk0 > 0)
{
//var arrStruc5 = MetaTypes.ConvertDataArray<Array_StructurePointer>(data, eoffset, (int)rk0);
//for (int n = 0; n < rk0; n++) arrStruc5[n].SwapEnd();
aOffset = offset + entry.DataOffset;
OpenTag(sb, indent, arrTag);
for (int n = 0; n < rk0; n++) //ARRAY ARRAY!
{
WriteArrayNode(sb, aind, cont, blockId, aOffset, arrEntry, estruct, "Item");
aOffset += 16;//ptr size... todo: what if not pointer array?
}
CloseTag(sb, indent, ename);
}
else
{
SelfClosingTag(sb, indent, arrTag);
}
break;
case PsoDataType.Structure:
switch (arrEntry.Unk_5h)
{
case 0:
break;
case 3://structure pointer array
var arrStrucPtr = MetaTypes.ConvertData<Array_StructurePointer>(data, eoffset);
arrStrucPtr.SwapEnd();
aBlockId = (int)arrStrucPtr.PointerDataId;
aOffset = (int)arrStrucPtr.PointerDataOffset;
aCount = arrStrucPtr.Count1;
if (aCount > 0)
{
var ptrArr = PsoTypes.GetPointerArray(cont.Pso, arrStrucPtr);
OpenTag(sb, indent, arrTag);
for (int n = 0; n < aCount; n++)
{
var ptrVal = ptrArr[n];
WriteNode(sb, aind, cont, ptrVal.BlockID, (int)ptrVal.ItemOffset, XmlTagMode.ItemAndType);
}
CloseTag(sb, indent, ename);
}
break;
default:
break;
}
arrTag += " itemType=\"" + HashString((MetaName)arrEntry.ReferenceKey) + "\"";
if (aCount > 0)
{
var aBlock = cont.Pso.GetBlock(aBlockId);
var atyp = cont.GetStructureInfo((MetaName)arrEntry.ReferenceKey);
if (aBlock == null)
{
ErrorXml(sb, indent, ename + ": Array block not found: " + aBlockId.ToString());
}
else if (aBlock.NameHash != MetaName.PsoPOINTER)
{
OpenTag(sb, indent, arrTag);
if (atyp == null)
{
ErrorXml(sb, indent, ename + ": Array type not found: " + HashString(arrEntry.ReferenceKey));
}
else
{
for (int n = 0; n < aCount; n++)
{
WriteNode(sb, aind, cont, aBlockId, aOffset, XmlTagMode.Item, (MetaName)arrEntry.ReferenceKey);
aOffset += atyp.StructureLength;
if ((n < (aCount - 1)) && (aBlock != null) && (aOffset >= aBlock.Length))
{
break;
}
}
}
CloseTag(sb, indent, ename);
}
else
{ } //pointer array should get here, but it's already handled above. should improve this.
}
else
{
SelfClosingTag(sb, indent, arrTag);
}
break;
case PsoDataType.String:
switch (entry.Unk_5h)
{
default:
ErrorXml(sb, indent, ename + ": Unexpected String array subtype: " + entry.Unk_5h.ToString());
break;
case 0: //hash array...
var arrHash = MetaTypes.ConvertData<Array_uint>(data, eoffset);
arrHash.SwapEnd();
var hashArr = PsoTypes.GetHashArray(cont.Pso, arrHash);
WriteItemArray(sb, hashArr, indent, ename, "Hash", HashString);
break;
}
break;
case PsoDataType.Float2:
aCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
arrTag += " itemType=\"Vector2\"";
var v2Arr = MetaTypes.ConvertDataArray<Vector2>(data, eoffset, (int)aCount);
WriteRawArray(sb, v2Arr, indent, ename, "Vector2", FormatVector2Swap, 1);
break;
case PsoDataType.Float3:
aCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
arrTag += " itemType=\"Vector3\""; //this is actually aligned as vector4, the W values are crazy in places
var v4Arr = MetaTypes.ConvertDataArray<Vector4>(data, eoffset, (int)aCount);
WriteRawArray(sb, v4Arr, indent, ename, "Vector3", FormatVector4SwapXYZOnly, 1);
break;
case PsoDataType.UByte:
var barr = new byte[aCount];
if (aCount > 0)
{
var bblock = cont.Pso.GetBlock(aBlockId);
var boffs = bblock.Offset + aOffset;
Buffer.BlockCopy(data, boffs, barr, 0, (int)aCount);
}
WriteRawArray(sb, barr, indent, ename, "byte");
break;
case PsoDataType.Bool:
var barr2 = new byte[aCount];
if (aCount > 0)
{
var bblock = cont.Pso.GetBlock(aBlockId);
var boffs = bblock.Offset + aOffset;
Buffer.BlockCopy(data, boffs, barr2, 0, (int)aCount);
}
WriteRawArray(sb, barr2, indent, ename, "boolean"); //todo: true/false output
break;
case PsoDataType.Float:
var arrFloat = MetaTypes.ConvertData<Array_float>(data, eoffset);
arrFloat.SwapEnd();
var floatArr = PsoTypes.GetFloatArray(cont.Pso, arrFloat);
WriteRawArray(sb, floatArr, indent, ename, "float");
break;
case PsoDataType.UShort:
var arrShort = MetaTypes.ConvertData<Array_Structure>(data, eoffset);
arrShort.SwapEnd();
var shortArr = PsoTypes.GetUShortArray(cont.Pso, arrShort);
WriteRawArray(sb, shortArr, indent, ename, "ushort");
break;
case PsoDataType.UInt:
var intArr = MetaTypes.ConvertDataArray<int>(data, eoffset, (int)aCount);
WriteRawArray(sb, intArr, indent, ename, "int");
break;
case PsoDataType.SInt:
var arrUint2 = MetaTypes.ConvertData<Array_uint>(data, eoffset);
arrUint2.SwapEnd();
var intArr2 = PsoTypes.GetUintArray(cont.Pso, arrUint2);
WriteRawArray(sb, intArr2, indent, ename, "int");
break;
case PsoDataType.Enum:
var arrEnum = MetaTypes.ConvertData<Array_uint>(data, eoffset);
arrEnum.SwapEnd();
var enumArr = PsoTypes.GetUintArray(cont.Pso, arrEnum);
var enumDef = cont.GetEnumInfo((MetaName)arrEntry.ReferenceKey);
WriteItemArray(sb, enumArr, indent, ename, "enum", (ie)=> {
var eval = enumDef?.FindEntry((int)ie);
return HashString(eval?.EntryNameHash ?? 0);
});
break;
}
}
private static void WriteMapNode(StringBuilder sb, int indent, PsoCont cont, int eoffset, PsoStructureEntryInfo entry, PsoStructureInfo structInfo, string ename)
{
var cind = indent + 1;
var data = cont.Pso.DataSection.Data;
switch (entry.Unk_5h)
{
default:
ErrorXml(sb, cind, ename + ": Unexpected Map subtype: " + entry.Unk_5h.ToString());
break;
case 1:
var mapidx1 = entry.ReferenceKey & 0x0000FFFF;
var mapidx2 = (entry.ReferenceKey >> 16) & 0x0000FFFF;
var mapreftype1 = structInfo.Entries[mapidx2];
var mapreftype2 = structInfo.Entries[mapidx1];
var x1 = MetaTypes.SwapBytes(BitConverter.ToInt32(data, eoffset));//same as ref key?
var x2 = MetaTypes.SwapBytes(BitConverter.ToInt32(data, eoffset + 4));//0?
var x3 = MetaTypes.SwapBytes(BitConverter.ToInt32(data, eoffset + 8));//pointer?
var x4 = MetaTypes.SwapBytes(BitConverter.ToInt32(data, eoffset + 12));//
var x5 = MetaTypes.SwapBytes(BitConverter.ToInt32(data, eoffset + 16));//count/capacity?
var x6 = MetaTypes.SwapBytes(BitConverter.ToInt32(data, eoffset + 20));//
//File.WriteAllText("C:\\CodeWalker.Projects\\testxml.xml", sb.ToString());
if (x1 != 0x1000000)
{ }
if (x2 != 0)
{ }
if (x4 != 0)
{ }
if (x6 != 0)
{ }
var xBlockId = x3 & 0xFFF;
var xOffset = (x3 >> 12) & 0xFFFFF;
var xCount1 = x5 & 0xFFFF;
var xCount2 = (x5 >> 16) & 0xFFFF;
//var x1a = x1 & 0xFFF; //block id? for another pointer?
//var x1b = (x1 >> 12) & 0xFFFFF; //offset?
//var x4u = (uint)x4;
//var x4a = x4 & 0xFFF; //block id?
//var x4b = (x4 >> 12) & 0xFFFFF; //offset?
//var x2h = (MetaHash)(uint)x2;
//var x6h = (MetaHash)(uint)x6;
//if (x1a > 0)
//{ }
var xBlock = cont.Pso.GetBlock(xBlockId);
if ((xBlock == null) && (xCount1 > 0))
{
ErrorXml(sb, cind, ename + ": Couldn't find Map xBlock: " + xBlockId.ToString());
}
else
{
if (xCount1 != xCount2)
{
}
if (xCount1 > 0)
{
var xStruct = cont.GetStructureInfo(xBlock.NameHash);
var xOffset1 = xOffset;
var xind = indent + 1;
var aind = indent + 2;
var kEntry = xStruct?.FindEntry(MetaName.Key);
var iEntry = xStruct?.FindEntry(MetaName.Item);
if ((xStruct == null) && (xBlock.NameHash == 0))
{
SelfClosingTag(sb, cind, ename);
}
else if (xStruct == null)
{
ErrorXml(sb, aind, ename + ": Map struct type not found: " + HashString(xBlock.NameHash));
}
else if ((xStruct.IndexInfo == null))// || (xStruct.IndexInfo.NameHash != MetaName.ARRAYINFO))
{
ErrorXml(sb, aind, ename + ": Map struct was missing IndexInfo! " + (xStruct == null ? "" : xStruct.ToString()));
}
else if ((kEntry == null) || (iEntry == null))
{
ErrorXml(sb, aind, ename + ": Map Key/Item entries not found!");
}
else if (kEntry.Type != PsoDataType.String)
{
ErrorXml(sb, aind, ename + ": Map Key was not a string!");
}
else if (iEntry.Type != PsoDataType.Structure)
{
ErrorXml(sb, aind, ename + ": Map Item was not a structure!");
}
else if (iEntry.Unk_5h != 3)
{
ErrorXml(sb, aind, ename + ": Map Item was not a structure pointer - TODO!");
}
else
{
OpenTag(sb, xind, ename);
int xOffset2 = (int)xOffset1;
int xCount = xCount1;
for (int n = 0; n < xCount; n++)
{
//WriteNode(sb, aind, cont, xBlockId, xOffset, XmlTagMode.Item, xStruct.IndexInfo.NameHash);
int sOffset = xOffset2 + xBlock.Offset;
var kOffset = sOffset + kEntry.DataOffset;
var iOffset = sOffset + iEntry.DataOffset;
var kStr = GetStringValue(cont.Pso, kEntry, data, kOffset);
var iPtr = MetaTypes.ConvertData<PsoPOINTER>(data, iOffset);
iPtr.SwapEnd();
var iBlock = cont.Pso.GetBlock(iPtr.BlockID);
if (iBlock == null)
{
OpenTag(sb, aind, "Item type=\"" + HashString((MetaName)entry.ReferenceKey) + "\" key=\"" + kStr + "\"");
WriteNode(sb, aind, cont, iPtr.BlockID, (int)iPtr.ItemOffset, XmlTagMode.None, (MetaName)entry.ReferenceKey);
CloseTag(sb, aind, "Item");
}
else
{
var iStr = "Item type=\"" + HashString(iBlock.NameHash) + "\" key=\"" + kStr + "\"";
var iStruc = cont.GetStructureInfo(iBlock.NameHash);
if (iStruc?.EntriesCount == 0)
{
//SelfClosingTag(sb, aind, iStr);
OpenTag(sb, aind, iStr);
CloseTag(sb, aind, "Item");
}
else
{
OpenTag(sb, aind, iStr);
WriteNode(sb, aind, cont, iPtr.BlockID, (int)iPtr.ItemOffset, XmlTagMode.None);//, (MetaName)entry.ReferenceKey);
CloseTag(sb, aind, "Item");
}
}
xOffset2 += xStruct.StructureLength;
if ((n < (xCount - 1)) && (xBlock != null) && (xOffset >= xBlock.Length))
{
ErrorXml(sb, aind, "Offset out of range! Count is " + xCount.ToString());
break; //out of range...
}
}
CloseTag(sb, xind, ename);
}
}
else
{
SelfClosingTag(sb, cind, ename);
}
}
break;
}
}
private static string GetStringValue(PsoFile pso, PsoStructureEntryInfo entry, byte[] data, int eoffset)
{
switch (entry.Unk_5h)
{
default:
return null;
case 0:
var str0len = (int)((entry.ReferenceKey >> 16) & 0xFFFF);
return Encoding.ASCII.GetString(data, eoffset, str0len).Replace("\0", "");
case 1:
case 2:
var dataPtr2 = MetaTypes.ConvertData<DataBlockPointer>(data, eoffset);
dataPtr2.SwapEnd();
return PsoTypes.GetString(pso, dataPtr2);
case 3:
var charPtr3 = MetaTypes.ConvertData<CharPointer>(data, eoffset);
charPtr3.SwapEnd();
var strval = PsoTypes.GetString(pso, charPtr3);
return strval ?? "";
case 7:
case 8:
var hashVal = MetaTypes.SwapBytes(MetaTypes.ConvertData<MetaHash>(data, eoffset));
return HashString(hashVal);
}
}
public class PsoCont
{
public PsoFile Pso { get; set; }
public Dictionary<MetaName, PsoEnumInfo> EnumDict = new Dictionary<MetaName, PsoEnumInfo>();
public Dictionary<MetaName, PsoStructureInfo> StructDict = new Dictionary<MetaName, PsoStructureInfo>();
public PsoCont(PsoFile pso)
{
Pso = pso;
if ((pso.SchemaSection == null) || (pso.SchemaSection.Entries == null) || (pso.SchemaSection.EntriesIdx == null))
{
return;
}
for (int i = 0; i < pso.SchemaSection.Entries.Length; i++)
{
var entry = pso.SchemaSection.Entries[i];
var enuminfo = entry as PsoEnumInfo;
var structinfo = entry as PsoStructureInfo;
if (enuminfo != null)
{
if (!EnumDict.ContainsKey(enuminfo.IndexInfo.NameHash))
{
EnumDict.Add(enuminfo.IndexInfo.NameHash, enuminfo);
}
else
{
//PsoEnumInfo oldei = EnumDict[enuminfo.IndexInfo.NameHash];
//if (!ComparePsoEnumInfos(oldei, enuminfo))
//{
//}
}
}
else if (structinfo != null)
{
if (!StructDict.ContainsKey(structinfo.IndexInfo.NameHash))
{
StructDict.Add(structinfo.IndexInfo.NameHash, structinfo);
}
else
{
//PsoStructureInfo oldsi = StructDict[structinfo.IndexInfo.NameHash];
//if (!ComparePsoStructureInfos(oldsi, structinfo))
//{
//}
}
}
}
}
public PsoStructureInfo GetStructureInfo(MetaName name)
{
PsoStructureInfo i = null;
StructDict.TryGetValue(name, out i);
return i;
}
public PsoEnumInfo GetEnumInfo(MetaName name)
{
PsoEnumInfo i = null;
EnumDict.TryGetValue(name, out i);
return i;
}
}
}
public class RbfXml : MetaXmlBase
{
public static string GetXml(RbfFile rbf)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
WriteNode(sb, 0, rbf.current);
return sb.ToString();
}
private static void WriteNode(StringBuilder sb, int indent, RbfStructure rs)
{
if (rs.Children.Count == 0)
{
SelfClosingTag(sb, indent, rs.Name);
return;
}
int cind = indent + 1;
bool oneline = ((rs.Children.Count == 1) && (rs.Children[0].Name == null));
OpenTag(sb, indent, rs.Name, !oneline);
foreach (var child in rs.Children)
{
if (child is RbfBytes)
{
var bytesChild = (RbfBytes)child;
var contentField = rs.FindChild("content") as RbfString;//TODO: fix this to output nicer XML!
if (contentField != null)
{
OpenTag(sb, cind, "value");
var aind = cind + 1;
if (contentField.Value == "char_array")
{
foreach (byte k in bytesChild.Value)
{
Indent(sb, aind);
sb.AppendLine(k.ToString());
}
}
else if (contentField.Value.Equals("short_array"))
{
var valueReader = new DataReader(new MemoryStream(bytesChild.Value));
while (valueReader.Position < valueReader.Length)
{
Indent(sb, aind);
var y = valueReader.ReadUInt16();
sb.AppendLine(y.ToString());
}
}
else
{
ErrorXml(sb, aind, "Unexpected content type: " + contentField.Value);
}
CloseTag(sb, cind, "value");
}
else
{
string stringValue = Encoding.ASCII.GetString(bytesChild.Value);
string str = stringValue.Substring(0, stringValue.Length - 1); //removes null terminator
sb.Append(str);
}
}
if (child is RbfFloat)
{
var floatChild = (RbfFloat)child;
ValueTag(sb, cind, child.Name, FloatUtil.ToString(floatChild.Value));
}
if (child is RbfString)
{
var stringChild = (RbfString)child;
StringTag(sb, cind, stringChild.Name, stringChild.Value);
//if (stringChild.Name.Equals("content"))
//else if (stringChild.Name.Equals("type"))
//else throw new Exception("Unexpected string content");
}
if (child is RbfStructure)
{
WriteNode(sb, cind, child as RbfStructure);
}
if (child is RbfUint32)
{
var intChild = (RbfUint32)child;
ValueTag(sb, cind, intChild.Name, UintString(intChild.Value));
}
if (child is RbfBoolean)
{
var booleanChild = (RbfBoolean)child;
ValueTag(sb, cind, booleanChild.Name, booleanChild.Value.ToString());
}
if (child is RbfFloat3)
{
var v3 = child as RbfFloat3;
SelfClosingTag(sb, cind, v3.Name + " x=\"" + FloatUtil.ToString(v3.X) + "\" y=\"" + FloatUtil.ToString(v3.Y) + "\" z=\"" + FloatUtil.ToString(v3.Z) + "\"");
}
}
CloseTag(sb, oneline ? 0 : indent, rs.Name);
}
}
public class MetaXmlBase
{
public const string XmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
public static void Indent(StringBuilder sb, int indent)
{
for (int i = 0; i < indent; i++)
{
sb.Append(" ");
}
}
public static void ErrorXml(StringBuilder sb, int indent, string msg)
{
Indent(sb, indent);
sb.Append("<error>");
sb.Append(msg);
sb.Append("</error>");
sb.AppendLine();
}
public static void OpenTag(StringBuilder sb, int indent, string name, bool appendLine = true)
{
Indent(sb, indent);
sb.Append("<");
sb.Append(name);
sb.Append(">");
if (appendLine) sb.AppendLine();
}
public static void CloseTag(StringBuilder sb, int indent, string name, bool appendLine = true)
{
Indent(sb, indent);
sb.Append("</");
sb.Append(name);
sb.Append(">");
if (appendLine) sb.AppendLine();
}
public static void ValueTag(StringBuilder sb, int indent, string name, string val)
{
Indent(sb, indent);
sb.Append("<");
sb.Append(name);
sb.Append(" value=\"");
sb.Append(val);
sb.Append("\" />");
sb.AppendLine();
}
public static void OneLineTag(StringBuilder sb, int indent, string name, string text)
{
Indent(sb, indent);
sb.Append("<");
sb.Append(name);
sb.Append(">");
sb.Append(text);
sb.Append("</");
sb.Append(name);
sb.Append(">");
sb.AppendLine();
}
public static void SelfClosingTag(StringBuilder sb, int indent, string val)
{
Indent(sb, indent);
sb.Append("<");
sb.Append(val);
sb.Append(" />");
sb.AppendLine();
}
public static void StringTag(StringBuilder sb, int indent, string name, string text)
{
if (!string.IsNullOrEmpty(text)) OneLineTag(sb, indent, name, text);
else SelfClosingTag(sb, indent, name);
}
public static void WriteRawArray<T>(StringBuilder sb, T[] arr, int ind, string name, string typeName, Func<T, string> formatter = null, int arrRowSize = 10) where T : struct
{
var aCount = arr?.Length ?? 0;
//var arrRowSize = 10;
var aind = ind + 1;
var arrTag = name;// + " itemType=\"" + typeName + "\"";
if (aCount > 0)
{
if (aCount <= arrRowSize)
{
OpenTag(sb, ind, arrTag, false);
for (int n = 0; n < aCount; n++)
{
if (n > 0) sb.Append(" ");
string str = (formatter != null) ? formatter(arr[n]) : arr[n].ToString();
sb.Append(str);
}
CloseTag(sb, 0, name);
}
else
{
OpenTag(sb, ind, arrTag);
for (int n = 0; n < aCount; n++)
{
var col = n % arrRowSize;
if (col == 0) Indent(sb, aind);
if (col > 0) sb.Append(" ");
string str = (formatter != null) ? formatter(arr[n]) : arr[n].ToString();
sb.Append(str);
bool lastcol = (col == (arrRowSize - 1));
bool lastn = (n == (aCount - 1));
if (lastcol || lastn) sb.AppendLine();
}
CloseTag(sb, ind, name);
}
}
else
{
SelfClosingTag(sb, ind, arrTag);
}
}
public static void WriteItemArray<T>(StringBuilder sb, T[] arr, int ind, string name, string typeName, Func<T, string> formatter) where T : struct
{
var aCount = arr?.Length ?? 0;
var arrTag = name;// + " itemType=\"Hash\"";
var aind = ind + 1;
if (aCount > 0)
{
OpenTag(sb, ind, arrTag);
for (int n = 0; n < aCount; n++)
{
Indent(sb, aind);
sb.Append("<Item>");
sb.Append(formatter(arr[n]));
sb.AppendLine("</Item>");
}
CloseTag(sb, ind, name);
}
else
{
SelfClosingTag(sb, ind, arrTag);
}
}
public static string FormatHash(MetaHash h) //for use with WriteItemArray
{
return h.ToString();
}
public static string FormatVector2(Vector2 v) //for use with WriteItemArray
{
return FloatUtil.GetVector2String(v);
}
public static string FormatVector3(Vector3 v) //for use with WriteItemArray
{
return FloatUtil.GetVector3String(v);
}
public static string FormatVector4(Vector4 v) //for use with WriteItemArray
{
return FloatUtil.GetVector4String(v);
}
public static string FormatHashSwap(MetaHash h) //for use with WriteItemArray, swaps endianness
{
return MetaTypes.SwapBytes(h).ToString();
}
public static string FormatVector2Swap(Vector2 v) //for use with WriteItemArray, swaps endianness
{
return FloatUtil.GetVector2String(MetaTypes.SwapBytes(v));
}
public static string FormatVector3Swap(Vector3 v) //for use with WriteItemArray, swaps endianness
{
return FloatUtil.GetVector3String(MetaTypes.SwapBytes(v));
}
public static string FormatVector4Swap(Vector4 v) //for use with WriteItemArray, swaps endianness
{
return FloatUtil.GetVector4String(MetaTypes.SwapBytes(v));
}
public static string FormatVector4SwapXYZOnly(Vector4 v) //for use with WriteItemArray, swaps endianness, and outputs only XYZ components
{
return FloatUtil.GetVector3String(MetaTypes.SwapBytes(v.XYZ()));
}
public static string HashString(MetaName h)
{
if (Enum.IsDefined(typeof(MetaName), h))
{
return h.ToString();
}
uint uh = (uint)h;
if (uh == 0) return "";
var str = JenkIndex.TryGetString(uh);
if (!string.IsNullOrEmpty(str)) return str;
//TODO: do extra hash lookup here
//if(Lookup.TryGetValue(uh, out str)) ...
return "hash_" + uh.ToString("X").PadLeft(8, '0');
}
public static string HashString(MetaHash h)
{
var str = JenkIndex.TryGetString(h);
if (string.IsNullOrEmpty(str))
{
var nh = (MetaName)(uint)h;
if (Enum.IsDefined(typeof(MetaName), nh))
{
return nh.ToString();
}
}
//todo: make sure JenkIndex is built!
//todo: do extra hash lookup here
if (h == 0) return "";
if (!string.IsNullOrEmpty(str)) return str;
return "hash_" + h.Hex;
}
public static string UintString(uint h)
{
if (Enum.IsDefined(typeof(MetaName), h))
{
return ((MetaName)h).ToString();
}
var str = JenkIndex.TryGetString(h);
if (!string.IsNullOrEmpty(str)) return str;
//TODO: do extra hash lookup here
//if(Lookup.TryGetValue(uh, out str)) ...
//if (h == 0) return "";
return "0x" + h.ToString("X");
}
public enum XmlTagMode
{
None = 0,
Structure = 1,
Item = 2,
ItemAndType = 3,
}
}
}