mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-08-20 17:09:19 +08:00
1684 lines
73 KiB
C#
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,
|
|
}
|
|
}
|
|
|
|
|
|
}
|