diff --git a/CodeWalker.Core/CodeWalker.Core.csproj b/CodeWalker.Core/CodeWalker.Core.csproj
index 3e40522..e1ee816 100644
--- a/CodeWalker.Core/CodeWalker.Core.csproj
+++ b/CodeWalker.Core/CodeWalker.Core.csproj
@@ -84,9 +84,11 @@
+
+
diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs
index 271f128..896868c 100644
--- a/CodeWalker.Core/GameFiles/GameFileCache.cs
+++ b/CodeWalker.Core/GameFiles/GameFileCache.cs
@@ -2212,16 +2212,27 @@ namespace CodeWalker.GameFiles
var exceptions = new List();
var allpsos = new List();
+ var diffpsos = new List();
foreach (RpfFile file in AllRpfs)
{
foreach (RpfEntry entry in file.AllEntries)
{
+#if !DEBUG
try
+#endif
{
var n = entry.NameLower;
+ if (!(n.EndsWith(".pso") ||
+ n.EndsWith(".ymt") ||
+ n.EndsWith(".ymf") ||
+ n.EndsWith(".ymap") ||
+ n.EndsWith(".ytyp") ||
+ n.EndsWith(".cut")))
+ continue; //PSO files seem to only have these extensions
+
var fentry = entry as RpfFileEntry;
- var data = entry.File.ExtractFile(fentry); //kind of slow, but sure to catch all PSO files
+ var data = entry.File.ExtractFile(fentry);
if (data != null)
{
using (MemoryStream ms = new MemoryStream(data))
@@ -2236,19 +2247,43 @@ namespace CodeWalker.GameFiles
allpsos.Add(fentry.Path);
PsoTypes.EnsurePsoTypes(pso);
+
+ var xml = PsoXml.GetXml(pso);
+ if (!string.IsNullOrEmpty(xml))
+ { }
+
+ var xdoc = new XmlDocument();
+ xdoc.LoadXml(xml);
+ var pso2 = XmlPso.GetPso(xdoc);
+ var pso2b = pso2.Save();
+
+ var pso3 = new PsoFile();
+ pso3.Load(pso2b);
+ var xml3 = PsoXml.GetXml(pso3);
+
+ if (xml.Length != xml3.Length)
+ { }
+ if (xml != xml3)
+ {
+ diffpsos.Add(fentry.Path);
+ }
+
}
}
}
}
+#if !DEBUG
catch (Exception ex)
{
UpdateStatus("Error! " + ex.ToString());
exceptions.Add(ex);
}
+#endif
}
}
string allpsopaths = string.Join("\r\n", allpsos);
+ string diffpsopaths = string.Join("\r\n", diffpsos);
string str = PsoTypes.GetTypesInitString();
if (!string.IsNullOrEmpty(str))
diff --git a/CodeWalker.Core/GameFiles/MetaTypes/Meta.cs b/CodeWalker.Core/GameFiles/MetaTypes/Meta.cs
index c617940..5d457c8 100644
--- a/CodeWalker.Core/GameFiles/MetaTypes/Meta.cs
+++ b/CodeWalker.Core/GameFiles/MetaTypes/Meta.cs
@@ -621,9 +621,9 @@ namespace CodeWalker.GameFiles
public ushort Count2 { get; set; }
public uint Unk1 { get; set; }
- public uint PointerDataId { get { return (Pointer & 0xFFF); } }
- public uint PointerDataIndex { get { return (Pointer & 0xFFF) - 1; } }
- public uint PointerDataOffset { get { return ((Pointer >> 12) & 0xFFFFF); } }
+ public uint PointerDataId { get { return (Pointer & 0xFFF); } set { Pointer = (Pointer & 0xFFFFF000) + (value & 0xFFF); } }
+ public uint PointerDataIndex { get { return (Pointer & 0xFFF) - 1; } set { PointerDataId = value + 1; } }
+ public uint PointerDataOffset { get { return ((Pointer >> 12) & 0xFFFFF); } set { Pointer = (Pointer & 0xFFF) + ((value << 12) & 0xFFFFF000); } }
diff --git a/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs b/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs
index c0000b1..0939b17 100644
--- a/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs
+++ b/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs
@@ -2007,6 +2007,10 @@ namespace CodeWalker.GameFiles
{
return (ushort)(((x & 0xFF00) >> 8) | ((x & 0x00FF) << 8));
}
+ public static short SwapBytes(short x)
+ {
+ return (short)SwapBytes((ushort)x);
+ }
public static uint SwapBytes(uint x)
{
// swap adjacent 16-bit blocks
diff --git a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs
index b40c0ef..453aaf8 100644
--- a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs
+++ b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Xml;
namespace CodeWalker.GameFiles
{
@@ -640,6 +641,10 @@ namespace CodeWalker.GameFiles
var structInfo = cont.GetStructureInfo(structName);
if (structInfo == null)
+ {
+ structInfo = PsoTypes.GetStructureInfo(structName);//fallback to builtin...
+ }
+ if (structInfo == null)
{
ErrorXml(sb, indent, "Couldn't find structure info " + name + "!");
return;
@@ -708,7 +713,10 @@ namespace CodeWalker.GameFiles
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));
+ var intH = HashString(intE?.EntryNameHash ?? 0);
+ if (string.IsNullOrEmpty(intH))
+ { }
+ StringTag(sb, cind, ename, intH);
break;
case 2: //byte enum
var byteEVal = data[eoffset];
@@ -718,9 +726,9 @@ namespace CodeWalker.GameFiles
}
break;
case PsoDataType.Flags:
- uint fCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
- uint fEntry = (entry.ReferenceKey & 0xFFFF);
- var fEnt = structInfo.GetEntry((int)fEntry);
+ //uint fCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
+ uint fEntry = (entry.ReferenceKey & 0xFFF);
+ var fEnt = (fEntry != 0xFFF) ? structInfo.GetEntry((int)fEntry) : null;
PsoEnumInfo flagsInfo = null;
if ((fEnt != null) && (fEnt.EntryNameHash == MetaName.ARRAYINFO))
{
@@ -728,7 +736,9 @@ namespace CodeWalker.GameFiles
}
if (flagsInfo == null)
{
- flagsInfo = cont.GetEnumInfo(entry.EntryNameHash);
+ if (fEntry != 0xFFF)
+ { }
+ //flagsInfo = cont.GetEnumInfo(entry.EntryNameHash);
}
uint? flagsVal = null;
switch (entry.Unk_5h)
@@ -805,13 +815,14 @@ namespace CodeWalker.GameFiles
default:
ErrorXml(sb, cind, ename + ": Unexpected Integer subtype: " + entry.Unk_5h.ToString());
break;
- case 0: //signed int
+ case 0: //signed int (? flags?)
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'));
+ ValueTag(sb, cind, ename, int6bVal.ToString());
+ //ValueTag(sb, cind, ename, "0x" + int6bVal.ToString("X").PadLeft(8, '0'));
break;
}
break;
@@ -833,16 +844,16 @@ namespace CodeWalker.GameFiles
ValueTag(sb, cind, ename, short4Val.ToString());
break;
case PsoDataType.HFloat://half float?
- var short1EVal = MetaTypes.SwapBytes(BitConverter.ToUInt16(data, eoffset));
+ var short1EVal = MetaTypes.SwapBytes(BitConverter.ToInt16(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
+ var str0 = XmlEscape(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);
}
@@ -909,8 +920,13 @@ namespace CodeWalker.GameFiles
var boffset = offset + block.Offset;
var eoffset = boffset + entry.DataOffset;
var aOffset = offset + entry.DataOffset;
+ var abOffset = aOffset + block.Offset;
var aBlockId = blockId;
uint aCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
+ Array_Structure arrStruc = new Array_Structure();
+ arrStruc.PointerDataId = (uint)aBlockId;
+ arrStruc.PointerDataOffset = (uint)aOffset;
+ arrStruc.Count1 = arrStruc.Count2 = (ushort)aCount;
var aind = indent + 1;
string arrTag = ename;
PsoStructureEntryInfo arrEntry = estruct.GetEntry((int)(entry.ReferenceKey & 0xFFFF));
@@ -922,17 +938,24 @@ namespace CodeWalker.GameFiles
var data = cont.Pso.DataSection.Data;
+ bool embedded = true;
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(data, eoffset);
+ arrStruc = MetaTypes.ConvertData(data, eoffset);
arrStruc.SwapEnd();
aBlockId = (int)arrStruc.PointerDataId;
aOffset = (int)arrStruc.PointerDataOffset;
aCount = arrStruc.Count1;
+ var aBlock = cont.Pso.GetBlock(aBlockId);
+ if (aBlock != null)
+ {
+ abOffset = aOffset + aBlock.Offset;
+ }
+ embedded = false;
break;
case 1: //Raw in-line array
break;
@@ -941,11 +964,20 @@ namespace CodeWalker.GameFiles
case 4: //pointer array? default array?
if (arrEntry.Unk_5h == 3) //pointers...
{
- var arrStruc4 = MetaTypes.ConvertData(data, eoffset);
- arrStruc4.SwapEnd();
- aBlockId = (int)arrStruc4.PointerDataId;
- aOffset = (int)arrStruc4.PointerDataOffset;
- aCount = arrStruc4.Count1;
+ arrStruc = MetaTypes.ConvertData(data, eoffset);
+ arrStruc.SwapEnd();
+ aBlockId = (int)arrStruc.PointerDataId;
+ aOffset = (int)arrStruc.PointerDataOffset;
+ aCount = arrStruc.Count1;
+ var aBlock2 = cont.Pso.GetBlock(aBlockId);
+ if (aBlock2 != null)
+ {
+ abOffset = aOffset + aBlock2.Offset;
+ }
+ embedded = false;
+ }
+ else
+ {
}
break;
case 129: //also raw inline array? in junctions.pso
@@ -959,10 +991,11 @@ namespace CodeWalker.GameFiles
break;
case PsoDataType.Array:
var rk0 = (entry.ReferenceKey >> 16) & 0x0000FFFF;
+ //var rk1 = entry.ReferenceKey & 0x0000FFFF;
+ //var rk3 = (arrEntry.ReferenceKey >> 16) & 0x0000FFFF;
+ //var rk4 = arrEntry.ReferenceKey & 0x0000FFFF;
if (rk0 > 0)
{
- //var arrStruc5 = MetaTypes.ConvertDataArray(data, eoffset, (int)rk0);
- //for (int n = 0; n < rk0; n++) arrStruc5[n].SwapEnd();
aOffset = offset + entry.DataOffset;
OpenTag(sb, indent, arrTag);
@@ -997,7 +1030,14 @@ namespace CodeWalker.GameFiles
for (int n = 0; n < aCount; n++)
{
var ptrVal = ptrArr[n];
- WriteNode(sb, aind, cont, ptrVal.BlockID, (int)ptrVal.ItemOffset, XmlTagMode.ItemAndType);
+ if (ptrVal.Pointer == 0)
+ {
+ SelfClosingTag(sb, aind, "Item"); //"null" entry...
+ }
+ else
+ {
+ WriteNode(sb, aind, cont, ptrVal.BlockID, (int)ptrVal.ItemOffset, XmlTagMode.ItemAndType);
+ }
}
CloseTag(sb, indent, ename);
}
@@ -1050,6 +1090,8 @@ namespace CodeWalker.GameFiles
ErrorXml(sb, indent, ename + ": Unexpected String array subtype: " + entry.Unk_5h.ToString());
break;
case 0: //hash array...
+ if (embedded)
+ { }
var arrHash = MetaTypes.ConvertData(data, eoffset);
arrHash.SwapEnd();
var hashArr = PsoTypes.GetHashArray(cont.Pso, arrHash);
@@ -1064,54 +1106,70 @@ namespace CodeWalker.GameFiles
WriteRawArray(sb, v2Arr, indent, ename, "Vector2", FormatVector2Swap, 1);
break;
case PsoDataType.Float3:
- aCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
+ if (!embedded)
+ { }
arrTag += " itemType=\"Vector3\""; //this is actually aligned as vector4, the W values are crazy in places
var v4Arr = MetaTypes.ConvertDataArray(data, eoffset, (int)aCount);
WriteRawArray(sb, v4Arr, indent, ename, "Vector3", FormatVector4SwapXYZOnly, 1);
break;
case PsoDataType.UByte:
+ if (embedded)
+ { }
+ else
+ { } //block type 2
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);
+ //var bblock = cont.Pso.GetBlock(aBlockId);
+ //var boffs = bblock.Offset + aOffset;
+ Buffer.BlockCopy(data, abOffset /*boffs*/, barr, 0, (int)aCount);
}
WriteRawArray(sb, barr, indent, ename, "byte");
break;
case PsoDataType.Bool:
+ if (embedded)
+ { }
+ else
+ { }
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);
+ //var bblock = cont.Pso.GetBlock(aBlockId);
+ //var boffs = bblock.Offset + aOffset;
+ Buffer.BlockCopy(data, abOffset /*boffs*/, barr2, 0, (int)aCount);
}
WriteRawArray(sb, barr2, indent, ename, "boolean"); //todo: true/false output
break;
case PsoDataType.Float:
- var arrFloat = MetaTypes.ConvertData(data, eoffset);
- arrFloat.SwapEnd();
+ if (embedded)
+ { }
+ var arrFloat = new Array_float(arrStruc.Pointer, arrStruc.Count1); //block type 7
var floatArr = PsoTypes.GetFloatArray(cont.Pso, arrFloat);
WriteRawArray(sb, floatArr, indent, ename, "float");
break;
case PsoDataType.UShort:
- var arrShort = MetaTypes.ConvertData(data, eoffset);
- arrShort.SwapEnd();
- var shortArr = PsoTypes.GetUShortArray(cont.Pso, arrShort);
+ if (embedded)
+ { }
+ var shortArr = PsoTypes.GetUShortArray(cont.Pso, arrStruc); //block type 4
WriteRawArray(sb, shortArr, indent, ename, "ushort");
break;
case PsoDataType.UInt:
- var intArr = MetaTypes.ConvertDataArray(data, eoffset, (int)aCount);
+ if (embedded)
+ { }
+ var arrUint = new Array_uint(arrStruc.Pointer, arrStruc.Count1); //block type 6
+ var intArr = PsoTypes.GetUintArray(cont.Pso, arrUint);
WriteRawArray(sb, intArr, indent, ename, "int");
break;
case PsoDataType.SInt:
- var arrUint2 = MetaTypes.ConvertData(data, eoffset);
- arrUint2.SwapEnd();
+ if (embedded)
+ { }
+ var arrUint2 = new Array_uint(arrStruc.Pointer, arrStruc.Count1); //block type 5
var intArr2 = PsoTypes.GetUintArray(cont.Pso, arrUint2);
WriteRawArray(sb, intArr2, indent, ename, "int");
break;
case PsoDataType.Enum:
+ if (embedded)
+ { }
var arrEnum = MetaTypes.ConvertData(data, eoffset);
arrEnum.SwapEnd();
var enumArr = PsoTypes.GetUintArray(cont.Pso, arrEnum);
@@ -1139,41 +1197,23 @@ namespace CodeWalker.GameFiles
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));//
+ var x1 = MetaTypes.SwapBytes(BitConverter.ToInt32(data, eoffset));
+ var x2 = MetaTypes.SwapBytes(BitConverter.ToInt32(data, eoffset + 4));
+ var sptr = MetaTypes.ConvertData(data, eoffset + 8);
+ sptr.SwapEnd();
- //File.WriteAllText("C:\\CodeWalker.Projects\\testxml.xml", sb.ToString());
if (x1 != 0x1000000)
{ }
if (x2 != 0)
{ }
- if (x4 != 0)
- { }
- if (x6 != 0)
+ if (mapreftype2.ReferenceKey != 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 xBlockId = (int)sptr.PointerDataId;// x3 & 0xFFF;
+ var xOffset = sptr.PointerDataOffset;// (x3 >> 12) & 0xFFFFF;
+ var xCount1 = sptr.Count1;// x5 & 0xFFFF;
+ var xCount2 = sptr.Count2;// (x5 >> 16) & 0xFFFF;
var xBlock = cont.Pso.GetBlock(xBlockId);
if ((xBlock == null) && (xCount1 > 0))
@@ -1183,8 +1223,7 @@ namespace CodeWalker.GameFiles
else
{
if (xCount1 != xCount2)
- {
- }
+ { }
if (xCount1 > 0)
{
var xStruct = cont.GetStructureInfo(xBlock.NameHash);
@@ -1218,10 +1257,10 @@ namespace CodeWalker.GameFiles
{
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 if (iEntry.Unk_5h != 3)
+ //{
+ // ErrorXml(sb, aind, ename + ": Map Item was not a structure pointer - TODO!");
+ //}
else
{
OpenTag(sb, xind, ename);
@@ -1236,30 +1275,39 @@ namespace CodeWalker.GameFiles
var kOffset = sOffset + kEntry.DataOffset;
var iOffset = sOffset + iEntry.DataOffset;
var kStr = GetStringValue(cont.Pso, kEntry, data, kOffset);
- var iPtr = MetaTypes.ConvertData(data, iOffset);
- iPtr.SwapEnd();
- var iBlock = cont.Pso.GetBlock(iPtr.BlockID);
- if (iBlock == null)
+ if (iEntry.ReferenceKey != 0)//(xBlock.NameHash != MetaName.ARRAYINFO)//257,258,259
{
- 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);
+ //embedded map values
+ var vOffset = xOffset2 + iEntry.DataOffset;
+ OpenTag(sb, aind, "Item type=\"" + HashString((MetaName)iEntry.ReferenceKey) + "\" key=\"" + kStr + "\"");
+ WriteNode(sb, aind, cont, xBlockId, vOffset, XmlTagMode.None, (MetaName)iEntry.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)
+ var iPtr = MetaTypes.ConvertData(data, iOffset);
+ iPtr.SwapEnd();
+ var iBlock = cont.Pso.GetBlock(iPtr.BlockID);
+ if (iBlock == null)
{
- //SelfClosingTag(sb, aind, iStr);
- OpenTag(sb, aind, iStr);
- CloseTag(sb, aind, "Item");
+ ErrorXml(sb, aind, ename + ": Could not find iBlock for Map entry!");
}
else
{
- OpenTag(sb, aind, iStr);
- WriteNode(sb, aind, cont, iPtr.BlockID, (int)iPtr.ItemOffset, XmlTagMode.None);//, (MetaName)entry.ReferenceKey);
- CloseTag(sb, aind, "Item");
+ 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;
@@ -1311,7 +1359,17 @@ namespace CodeWalker.GameFiles
}
-
+ public static string XmlEscape(string unescaped)
+ {
+ if (unescaped == null) return null;
+ XmlDocument doc = new XmlDocument();
+ XmlNode node = doc.CreateElement("root");
+ node.InnerText = unescaped;
+ var escaped = node.InnerXml;
+ if (escaped != unescaped)
+ { }
+ return node.InnerXml;
+ }
public class PsoCont
diff --git a/CodeWalker.Core/GameFiles/MetaTypes/Pso.cs b/CodeWalker.Core/GameFiles/MetaTypes/Pso.cs
index a92ff3e..f63fa26 100644
--- a/CodeWalker.Core/GameFiles/MetaTypes/Pso.cs
+++ b/CodeWalker.Core/GameFiles/MetaTypes/Pso.cs
@@ -205,6 +205,13 @@ namespace CodeWalker.GameFiles
public PsoSTRESection STRESection { get; set; }
public PsoCHKSSection CHKSSection { get; set; }
+
+ public void Load(byte[] data)
+ {
+ using (var ms = new MemoryStream(data))
+ Load(ms);
+ }
+
public void Load(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Open))
@@ -268,6 +275,19 @@ namespace CodeWalker.GameFiles
}
}
+
+ public byte[] Save()
+ {
+ var ms = new MemoryStream();
+ Save(ms);
+
+ var buf = new byte[ms.Length];
+ ms.Position = 0;
+ ms.Read(buf, 0, buf.Length);
+
+ return buf;
+ }
+
public void Save(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Create))
@@ -280,6 +300,11 @@ namespace CodeWalker.GameFiles
if (DataSection != null) DataSection.Write(writer);
if (DataMapSection != null) DataMapSection.Write(writer);
if (SchemaSection != null) SchemaSection.Write(writer);
+ if (STRFSection != null) STRFSection.Write(writer);
+ if (STRSSection != null) STRSSection.Write(writer);
+ if (STRESection != null) STRESection.Write(writer);
+ if (PSIGSection != null) PSIGSection.Write(writer);
+ if (CHKSSection != null) CHKSSection.Write(writer);
}
@@ -342,11 +367,13 @@ namespace CodeWalker.GameFiles
public void Write(DataWriter writer)
{
+ Length = Data.Length;
+
writer.Write(Data);
- writer.Position -= Data.Length;
- writer.Write((uint)0x5053494E);
- writer.Write((uint)(Data.Length));
- writer.Position += Data.Length - 8;
+ writer.Position -= Length;
+ writer.Write(Ident);
+ writer.Write((uint)(Length));
+ writer.Position += Length - 8;
}
public override string ToString()
@@ -413,10 +440,10 @@ namespace CodeWalker.GameFiles
public void Read(DataReader reader)
{
- this.NameHash = (MetaName)reader.ReadUInt32();
- this.Offset = reader.ReadInt32();
- this.Unknown_8h = reader.ReadInt32();
- this.Length = reader.ReadInt32();
+ NameHash = (MetaName)reader.ReadUInt32();
+ Offset = reader.ReadInt32();
+ Unknown_8h = reader.ReadInt32();
+ Length = reader.ReadInt32();
}
public void Write(DataWriter writer)
@@ -449,7 +476,7 @@ namespace CodeWalker.GameFiles
Length = reader.ReadInt32();
Count = reader.ReadUInt32();
- this.EntriesIdx = new PsoElementIndexInfo[Count];
+ EntriesIdx = new PsoElementIndexInfo[Count];
for (int i = 0; i < Count; i++)
{
var entry = new PsoElementIndexInfo();
@@ -457,7 +484,7 @@ namespace CodeWalker.GameFiles
EntriesIdx[i] = entry;
}
- this.Entries = new PsoElementInfo[Count];
+ Entries = new PsoElementInfo[Count];
for (int i = 0; i < Count; i++)
{
reader.Position = EntriesIdx[i].Offset;
@@ -506,7 +533,7 @@ namespace CodeWalker.GameFiles
writer.Write(Ident);
writer.Write((int)(12 + entriesStream.Length + indexStream.Length));
- writer.Write((int)(Entries.Length));
+ writer.Write((uint)(Entries.Length));
// write entries index data
var buf1 = new byte[indexStream.Length];
@@ -536,8 +563,8 @@ namespace CodeWalker.GameFiles
public void Read(DataReader reader)
{
- this.NameHash = (MetaName)reader.ReadUInt32();
- this.Offset = reader.ReadInt32();
+ NameHash = (MetaName)reader.ReadUInt32();
+ Offset = reader.ReadInt32();
}
public void Write(DataWriter writer)
@@ -590,11 +617,11 @@ namespace CodeWalker.GameFiles
public override void Read(DataReader reader)
{
uint x = reader.ReadUInt32();
- this.Type = (byte)((x & 0xFF000000) >> 24);
- this.EntriesCount = (short)(x & 0xFFFF);
- this.Unk = (byte)((x & 0x00FF0000) >> 16);
- this.StructureLength = reader.ReadInt32();
- this.Unk_Ch = reader.ReadUInt32();
+ Type = (byte)((x & 0xFF000000) >> 24);
+ EntriesCount = (short)(x & 0xFFFF);
+ Unk = (byte)((x & 0x00FF0000) >> 16);
+ StructureLength = reader.ReadInt32();
+ Unk_Ch = reader.ReadUInt32();
if (Unk_Ch != 0)
{ }
@@ -655,7 +682,7 @@ namespace CodeWalker.GameFiles
{
public MetaName EntryNameHash { get; set; }
public PsoDataType Type { get; set; }
- public byte Unk_5h { get; set; }
+ public byte Unk_5h { get; set; } //0 = default, 3 = pointer array?
public ushort DataOffset { get; set; }
public uint ReferenceKey { get; set; } // when array -> entry index with type
@@ -673,11 +700,11 @@ namespace CodeWalker.GameFiles
public void Read(DataReader reader)
{
- this.EntryNameHash = (MetaName)reader.ReadUInt32();
- this.Type = (PsoDataType)reader.ReadByte();
- this.Unk_5h = reader.ReadByte();
- this.DataOffset = reader.ReadUInt16();
- this.ReferenceKey = reader.ReadUInt32();
+ EntryNameHash = (MetaName)reader.ReadUInt32();
+ Type = (PsoDataType)reader.ReadByte();
+ Unk_5h = reader.ReadByte();
+ DataOffset = reader.ReadUInt16();
+ ReferenceKey = reader.ReadUInt32();
}
public void Write(DataWriter writer)
@@ -721,8 +748,8 @@ namespace CodeWalker.GameFiles
public override void Read(DataReader reader)
{
uint x = reader.ReadUInt32();
- this.Type = (byte)((x & 0xFF000000) >> 24);
- this.EntriesCount = (int)(x & 0x00FFFFFF);
+ Type = (byte)((x & 0xFF000000) >> 24);
+ EntriesCount = (int)(x & 0x00FFFFFF);
Entries = new PsoEnumEntryInfo[EntriesCount];
for (int i = 0; i < EntriesCount; i++)
@@ -762,6 +789,21 @@ namespace CodeWalker.GameFiles
return null;
}
+ public PsoEnumEntryInfo FindEntryByName(MetaName name)
+ {
+ if (Entries == null) return null;
+ for (int i = 0; i < Entries.Length; i++)
+ {
+ var entry = Entries[i];
+ if (entry.EntryNameHash == name)
+ {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+
public override string ToString()
{
@@ -785,8 +827,8 @@ namespace CodeWalker.GameFiles
public void Read(DataReader reader)
{
- this.EntryNameHash = (MetaName)reader.ReadUInt32();
- this.EntryKey = reader.ReadInt32();
+ EntryNameHash = (MetaName)reader.ReadUInt32();
+ EntryKey = reader.ReadInt32();
}
public void Write(DataWriter writer)
@@ -827,10 +869,25 @@ namespace CodeWalker.GameFiles
public void Write(DataWriter writer)
{
+ var strStream = new MemoryStream();
+ var strWriter = new DataWriter(strStream, Endianess.BigEndian);
+ foreach (var str in Strings)
+ {
+ strWriter.Write(str);
+ }
+
+ Length = (int)strStream.Length + 8;
writer.Write(Ident);
writer.Write(Length);
+ if (strStream.Length > 0)
+ {
+ var buf1 = new byte[strStream.Length];
+ strStream.Position = 0;
+ strStream.Read(buf1, 0, buf1.Length);
+ writer.Write(buf1);
+ }
}
@@ -867,10 +924,25 @@ namespace CodeWalker.GameFiles
public void Write(DataWriter writer)
{
+ var strStream = new MemoryStream();
+ var strWriter = new DataWriter(strStream, Endianess.BigEndian);
+ foreach (var str in Strings)
+ {
+ strWriter.Write(str);
+ }
+
+ Length = (int)strStream.Length + 8;
writer.Write(Ident);
writer.Write(Length);
+ if (strStream.Length > 0)
+ {
+ var buf1 = new byte[strStream.Length];
+ strStream.Position = 0;
+ strStream.Read(buf1, 0, buf1.Length);
+ writer.Write(buf1);
+ }
}
@@ -886,7 +958,6 @@ namespace CodeWalker.GameFiles
public int Length { get; set; }
public byte[] Data { get; set; }
- //public MetaHash[] Hashes { get; set; }
//public byte[] Decr1 { get; set; }
//public byte[] Decr2 { get; set; }
@@ -899,26 +970,25 @@ namespace CodeWalker.GameFiles
{
Data = reader.ReadBytes(Length - 8);
- //reader.Position = 8;
- //List hashes = new List();
- //while (reader.Position < reader.Length)
- //{
- // hashes.Add(reader.ReadUInt32());
- //}
- //Hashes = hashes.ToArray();
-
//Decr1 = GTACrypto.DecryptAES(Data);
//Decr2 = GTACrypto.DecryptNG(Data, )
+ //TODO: someone plz figure out that encryption
}
}
public void Write(DataWriter writer)
{
+ Length = (Data?.Length??0) + 8;
+
writer.Write(Ident);
writer.Write(Length);
+ if (Length > 8)
+ {
+ writer.Write(Data);
+ }
}
@@ -947,10 +1017,15 @@ namespace CodeWalker.GameFiles
public void Write(DataWriter writer)
{
+ Length = (Data?.Length ?? 0) + 8;
writer.Write(Ident);
writer.Write(Length);
+ if (Length > 8)
+ {
+ writer.Write(Data);
+ }
}
@@ -983,11 +1058,13 @@ namespace CodeWalker.GameFiles
public void Write(DataWriter writer)
{
+ Length = 20;
writer.Write(Ident);
writer.Write(Length);
-
-
+ writer.Write(FileSize);
+ writer.Write(Checksum);
+ writer.Write(Unk0);
}
public override string ToString()
diff --git a/CodeWalker.Core/GameFiles/MetaTypes/PsoBuilder.cs b/CodeWalker.Core/GameFiles/MetaTypes/PsoBuilder.cs
new file mode 100644
index 0000000..ceeb96f
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/MetaTypes/PsoBuilder.cs
@@ -0,0 +1,504 @@
+using SharpDX;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ public class PsoBuilder
+ {
+
+
+ public PsoBuilderPointer RootPointer { get; set; }
+
+ List STRFStrings = new List();
+ List STRSStrings = new List();
+
+
+ Dictionary StructureInfos = new Dictionary();
+ Dictionary EnumInfos = new Dictionary();
+
+
+ List Blocks = new List();
+ int MaxBlockLength = 0x100000; //TODO: figure what this should be!
+
+ public PsoBuilderBlock EnsureBlock(MetaName type)
+ {
+ foreach (var block in Blocks)
+ {
+ if (block.StructureNameHash == type)
+ {
+ if (block.TotalSize < MaxBlockLength)
+ {
+ return block;
+ }
+ }
+ }
+ PsoBuilderBlock b = new PsoBuilderBlock();
+ b.StructureNameHash = type;
+ b.Index = Blocks.Count;
+ Blocks.Add(b);
+ return b;
+ }
+
+
+ public PsoBuilderPointer AddItem(MetaName type, T item) where T : struct
+ {
+ byte[] data = MetaTypes.ConvertToBytes(item);
+ return AddItem(type, data);
+ }
+
+ public PsoBuilderPointer AddItem(MetaName type, byte[] data)
+ {
+ PsoBuilderBlock block = EnsureBlock(type);
+ int brem = data.Length % 16;
+ if (brem > 0)
+ {
+ int newlen = data.Length - brem + 16;
+ byte[] newdata = new byte[newlen];
+ Buffer.BlockCopy(data, 0, newdata, 0, data.Length);
+ data = newdata; //make sure item size is multiple of 16... so pointers don't need sub offsets!
+ }
+ int idx = block.AddItem(data);
+ PsoBuilderPointer r = new PsoBuilderPointer();
+ r.BlockID = block.Index + 1;
+ r.Offset = (idx * data.Length);
+ r.Length = data.Length;
+ return r;
+ }
+
+ public PsoBuilderPointer AddItemArray(MetaName type, T[] items) where T : struct
+ {
+ byte[] data = MetaTypes.ConvertArrayToBytes(items);
+ return AddItemArray(type, data, items.Length);
+ }
+
+ public PsoBuilderPointer AddItemArray(MetaName type, byte[] data, int length)
+ {
+ PsoBuilderBlock block = EnsureBlock(type);
+ int datalen = data.Length;
+ int newlen = datalen;
+ //int lenrem = newlen % 16;
+ //if (lenrem != 0)
+ //{
+ // newlen += (16 - lenrem);
+ //}
+ byte[] newdata = new byte[newlen];
+ Buffer.BlockCopy(data, 0, newdata, 0, datalen);
+ int offs = block.TotalSize;
+ int idx = block.AddItem(newdata);
+ PsoBuilderPointer r = new PsoBuilderPointer();
+ r.BlockID = block.Index + 1;
+ r.Offset = offs; //(idx * data.Length);;
+ r.Length = length;
+ return r;
+ }
+
+
+
+ public PsoPOINTER AddItemPtr(MetaName type, T item) where T : struct //helper method for AddItem
+ {
+ var ptr = AddItem(type, item);
+ return new PsoPOINTER(ptr.BlockID, ptr.Offset, 0);
+ }
+
+ public PsoPOINTER AddItemPtr(MetaName type, byte[] data)//helper method for AddItem
+ {
+ var ptr = AddItem(type, data);
+ return new PsoPOINTER(ptr.BlockID, ptr.Offset, 0);
+ }
+
+ public Array_Structure AddItemArrayPtr(MetaName type, T[] items) where T : struct //helper method for AddItemArray
+ {
+ if ((items == null) || (items.Length == 0)) return new Array_Structure();
+ var ptr = AddItemArray(type, items);
+ return new Array_Structure(ptr.Pointer, ptr.Length);
+ }
+
+ public Array_Structure AddItemArrayPtr(MetaName type, byte[][] data) //helper method for AddItemArray
+ {
+ if ((data == null) || (data.Length == 0)) return new Array_Structure();
+
+ int len = 0;
+
+ for (int i = 0; i < data.Length; i++)
+ {
+ len += data[i].Length;
+ }
+
+ var newdata = new byte[len];
+
+ int offset = 0;
+
+ for (int i = 0; i < data.Length; i++)
+ {
+ Buffer.BlockCopy(data[i], 0, newdata, offset, data[i].Length);
+ offset += data[i].Length;
+ }
+
+ var ptr = AddItemArray(type, newdata, data.Length);
+ return new Array_Structure(ptr.Pointer, ptr.Length);
+ }
+
+
+
+ public Array_StructurePointer AddPointerArray(PsoPOINTER[] arr)
+ {
+ if ((arr == null) || (arr.Length == 0)) return new Array_StructurePointer();
+ var ptr = AddItemArray(MetaName.PsoPOINTER, arr);
+ Array_StructurePointer sp = new Array_StructurePointer();
+ sp.Count1 = (ushort)arr.Length;
+ sp.Count2 = sp.Count1;
+ sp.Pointer = ptr.Pointer;
+ return sp;
+ }
+
+
+
+ public PsoBuilderPointer AddString(string str)
+ {
+ PsoBuilderBlock block = EnsureBlock((MetaName)1); //PsoSTRING seems to be 1
+ byte[] data = Encoding.ASCII.GetBytes(str + (char)0);
+ int datalen = data.Length;
+ int newlen = datalen;
+ //int lenrem = newlen % 16;
+ //if (lenrem != 0) //pad the data length up to multiple of 16.
+ //{
+ // newlen += (16 - lenrem);
+ //}
+ byte[] newdata = new byte[newlen];
+ Buffer.BlockCopy(data, 0, newdata, 0, datalen);
+ int offs = block.TotalSize;
+ int idx = block.AddItem(newdata);
+ PsoBuilderPointer r = new PsoBuilderPointer();
+ r.BlockID = block.Index + 1;
+ r.Offset = offs;// (idx * data.Length);
+ r.Length = datalen; //actual length of string.
+ return r;
+ }
+
+
+ public Array_Vector3 AddPaddedVector3ArrayPtr(Vector4[] items)
+ {
+ if ((items == null) || (items.Length == 0)) return new Array_Vector3();
+ var ptr = AddItemArray((MetaName)1, items); //MetaName.VECTOR4 padded to vec4...
+ return new Array_Vector3(ptr.Pointer, items.Length);
+ }
+ public Array_Vector3 AddVector2ArrayPtr(Vector2[] items)
+ {
+ if ((items == null) || (items.Length == 0)) return new Array_Vector3();
+ var ptr = AddItemArray((MetaName)1, items); //MetaName.VECTOR4 padded to vec4...?
+ return new Array_Vector3(ptr.Pointer, items.Length);
+ }
+ public Array_uint AddHashArrayPtr(MetaHash[] items)
+ {
+ if ((items == null) || (items.Length == 0)) return new Array_uint();
+ var ptr = AddItemArray((MetaName)6, items); //MetaName.HASH
+ return new Array_uint(ptr.Pointer, items.Length);
+ }
+ public Array_uint AddUIntArrayPtr(uint[] items)
+ {
+ if ((items == null) || (items.Length == 0)) return new Array_uint();
+ var ptr = AddItemArray((MetaName)6, items);
+ return new Array_uint(ptr.Pointer, items.Length);
+ }
+ public Array_uint AddSIntArrayPtr(int[] items)
+ {
+ if ((items == null) || (items.Length == 0)) return new Array_uint();
+ var ptr = AddItemArray((MetaName)5, items);
+ return new Array_uint(ptr.Pointer, items.Length);
+ }
+ public Array_ushort AddUShortArrayPtr(ushort[] items)
+ {
+ if ((items == null) || (items.Length == 0)) return new Array_ushort();
+ var ptr = AddItemArray((MetaName)4, items);
+ return new Array_ushort(ptr.Pointer, items.Length);
+ }
+ public Array_byte AddByteArrayPtr(byte[] items)
+ {
+ if ((items == null) || (items.Length == 0)) return new Array_byte();
+ var ptr = AddItemArray((MetaName)2, items);
+ return new Array_byte(ptr.Pointer, items.Length);
+ }
+ public Array_float AddFloatArrayPtr(float[] items)
+ {
+ if ((items == null) || (items.Length == 0)) return new Array_float();
+ var ptr = AddItemArray((MetaName)7, items); //MetaName.PsoFLOAT ? comes up as MetaName.POINTER due to RSC meta values
+ return new Array_float(ptr.Pointer, items.Length);
+ }
+
+
+
+
+ public void AddStringToSTRF(string str)
+ {
+ STRFStrings.Add(str);
+ }
+ public void AddStringToSTRS(string str)
+ {
+ STRSStrings.Add(str);
+ }
+
+
+
+
+ public void AddStructureInfo(MetaName name)
+ {
+ if (!StructureInfos.ContainsKey(name))
+ {
+ PsoStructureInfo si = PsoTypes.GetStructureInfo(name);
+ if (si != null)
+ {
+ StructureInfos[name] = si;
+ }
+ }
+ }
+ public void AddEnumInfo(MetaName name)
+ {
+ if (!EnumInfos.ContainsKey(name))
+ {
+ PsoEnumInfo ei = PsoTypes.GetEnumInfo(name);
+ if (ei != null)
+ {
+ EnumInfos[name] = ei;
+ }
+ }
+ }
+
+
+ public PsoStructureInfo AddMapNodeStructureInfo(MetaName valType)
+ {
+ PsoStructureInfo inf = null;
+
+ if (valType == 0)
+ {
+ inf = PsoTypes.GetStructureInfo(MetaName.ARRAYINFO); //default ARRAYINFO with pointer
+ if (!StructureInfos.ContainsKey(inf.IndexInfo.NameHash))
+ {
+ StructureInfos[inf.IndexInfo.NameHash] = inf;
+ }
+ return inf;
+ }
+
+ var structInfo = PsoTypes.GetStructureInfo(valType);
+ if (structInfo == null)
+ { }//error?
+
+ MetaName xName = MetaName.ARRAYINFO + 1; //257
+ bool nameOk = !StructureInfos.ContainsKey(xName);
+ while (!nameOk)
+ {
+ var exInfo = StructureInfos[xName];
+ var exInfoItem = exInfo.FindEntry(MetaName.Item);
+ if (((MetaName)(exInfoItem?.ReferenceKey ?? 0) == valType))
+ {
+ return exInfo; //this one already exists.. use it!
+ }
+ xName++;
+ nameOk = !StructureInfos.ContainsKey(xName);
+ }
+
+
+
+ inf = new PsoStructureInfo(xName, 0, 2, 8 + structInfo.StructureLength,
+ new PsoStructureEntryInfo(MetaName.Key, PsoDataType.String, 0, 7, 0),
+ new PsoStructureEntryInfo(MetaName.Item, PsoDataType.Structure, 8, 0, valType)
+ );
+
+ if (!StructureInfos.ContainsKey(xName))
+ {
+ StructureInfos[xName] = inf;
+ }
+
+ return inf;
+
+
+ //switch (valType)
+ //{
+ // case (MetaName)331140115: return PsoTypes.GetStructureInfo((MetaName)257);
+ // case (MetaName)2046450505: return PsoTypes.GetStructureInfo((MetaName)258);
+ // case (MetaName)3219912345: return PsoTypes.GetStructureInfo((MetaName)259);
+ // case (MetaName)0: return PsoTypes.GetStructureInfo(MetaName.ARRAYINFO);
+ // default:
+ // return PsoTypes.GetStructureInfo(MetaName.ARRAYINFO);//error?
+ //}
+ //case (MetaName)257:
+ // return new PsoStructureInfo((MetaName)257, 0, 2, 32,
+ // new PsoStructureEntryInfo(MetaName.Key, PsoDataType.String, 0, 7, 0),
+ // new PsoStructureEntryInfo(MetaName.Item, PsoDataType.Structure, 8, 0, (MetaName)331140115)
+ // );
+ //case (MetaName)258:
+ // return new PsoStructureInfo((MetaName)258, 0, 2, 24,
+ // new PsoStructureEntryInfo(MetaName.Key, PsoDataType.String, 0, 7, 0),
+ // new PsoStructureEntryInfo(MetaName.Item, PsoDataType.Structure, 8, 0, (MetaName)2046450505)
+ // );
+ //case (MetaName)259:
+ // return new PsoStructureInfo((MetaName)259, 0, 2, 32,
+ // new PsoStructureEntryInfo(MetaName.Key, PsoDataType.String, 0, 7, 0),
+ // new PsoStructureEntryInfo(MetaName.Item, PsoDataType.Structure, 8, 0, (MetaName)3219912345)
+ // );
+ //case (MetaName)3219912345:
+ // return new PsoStructureInfo((MetaName)3219912345, 0, 0, 24,
+ // new PsoStructureEntryInfo(MetaName.ARRAYINFO, PsoDataType.Structure, 0, 0, (MetaName)2356519750),
+ // new PsoStructureEntryInfo((MetaName)4147768898, PsoDataType.Array, 8, 0, 0)
+ // );
+ }
+
+
+
+
+ public byte[] GetData()
+ {
+ int totlen = 16;
+ for (int i = 0; i < Blocks.Count; i++)
+ {
+ totlen += Blocks[i].TotalSize;
+ }
+ byte[] data = new byte[totlen];
+ int offset = 16; //reserved space for headers
+ for (int i = 0; i < Blocks.Count; i++)
+ {
+ var block = Blocks[i];
+ for (int j = 0; j < block.Items.Count; j++)
+ {
+ var bdata = block.Items[j];
+ Buffer.BlockCopy(bdata, 0, data, offset, bdata.Length);
+ offset += bdata.Length;
+ }
+ }
+ if (offset != data.Length)
+ { }
+ return data;
+ }
+
+
+
+
+
+
+ public PsoFile GetPso()
+ {
+ PsoFile pso = new PsoFile();
+ pso.SchemaSection = new PsoSchemaSection();
+
+ var schEntries = new List();
+ foreach (var structInfo in StructureInfos.Values)
+ {
+ schEntries.Add(structInfo);
+ }
+ foreach (var enumInfo in EnumInfos.Values)
+ {
+ schEntries.Add(enumInfo);
+ }
+ pso.SchemaSection.Entries = schEntries.ToArray();
+ pso.SchemaSection.EntriesIdx = new PsoElementIndexInfo[schEntries.Count];
+ for (int i = 0; i < schEntries.Count; i++)
+ {
+ pso.SchemaSection.EntriesIdx[i] = new PsoElementIndexInfo();
+ pso.SchemaSection.EntriesIdx[i].NameHash = schEntries[i].IndexInfo.NameHash;
+ }
+
+ if (STRFStrings.Count > 0)
+ {
+ pso.STRFSection = new PsoSTRFSection();
+ pso.STRFSection.Strings = STRFStrings.ToArray();
+ }
+ if (STRSStrings.Count > 0)
+ {
+ pso.STRSSection = new PsoSTRSSection();
+ pso.STRSSection.Strings = STRSStrings.ToArray();
+ }
+
+
+ pso.DataSection = new PsoDataSection();
+ pso.DataSection.Data = GetData();
+
+ pso.DataMapSection = new PsoDataMapSection();
+ pso.DataMapSection.Entries = new PsoDataMappingEntry[Blocks.Count];
+ pso.DataMapSection.RootId = RootPointer.BlockID;
+ var offset = 16;
+ for (int i = 0; i < Blocks.Count; i++)
+ {
+ var b = Blocks[i];
+ var e = new PsoDataMappingEntry();
+ e.NameHash = b.StructureNameHash;
+ e.Length = b.TotalSize;
+ e.Offset = offset;
+ offset += b.TotalSize;
+ pso.DataMapSection.Entries[i] = e;
+ }
+
+
+
+ return pso;
+ }
+
+ }
+
+
+
+ public class PsoBuilderBlock
+ {
+ public MetaName StructureNameHash { get; set; }
+ public List Items { get; set; } = new List();
+ public int TotalSize { get; set; } = 0;
+ public int Index { get; set; } = 0;
+
+ public int AddItem(byte[] item)
+ {
+ int idx = Items.Count;
+ Items.Add(item);
+ TotalSize += item.Length;
+ return idx;
+ }
+
+ public uint BasePointer
+ {
+ get
+ {
+ return (((uint)Index + 1) & 0xFFF);
+ }
+ }
+
+
+ //public MetaDataBlock GetMetaDataBlock()
+ //{
+ // if (TotalSize <= 0) return null;
+ // byte[] data = new byte[TotalSize];
+ // int offset = 0;
+ // for (int j = 0; j < Items.Count; j++)
+ // {
+ // var bdata = Items[j];
+ // Buffer.BlockCopy(bdata, 0, data, offset, bdata.Length);
+ // offset += bdata.Length;
+ // }
+ // MetaDataBlock db = new MetaDataBlock();
+ // db.StructureNameHash = StructureNameHash;
+ // db.DataLength = TotalSize;
+ // db.Data = data;
+ // return db;
+ //}
+
+
+ }
+
+ public struct PsoBuilderPointer
+ {
+ public int BlockID { get; set; } //1-based id
+ public int Offset { get; set; } //byte offset
+ public int Length { get; set; } //for temp use...
+ public uint Pointer
+ {
+ get
+ {
+ uint bidx = (((uint)BlockID) & 0xFFF);
+ uint offs = (((uint)Offset) & 0xFFFFF) << 12;
+ return bidx + offs;
+ }
+ }
+ }
+
+
+}
diff --git a/CodeWalker.Core/GameFiles/MetaTypes/PsoTypes.cs b/CodeWalker.Core/GameFiles/MetaTypes/PsoTypes.cs
index 83a5c58..8f148ff 100644
--- a/CodeWalker.Core/GameFiles/MetaTypes/PsoTypes.cs
+++ b/CodeWalker.Core/GameFiles/MetaTypes/PsoTypes.cs
@@ -647,11 +647,11 @@ namespace CodeWalker.GameFiles
new PsoStructureEntryInfo(MetaName.Probabilities, PsoDataType.Array, 8, 0, 0)
);
case (MetaName)2575850962:
- return new PsoStructureInfo((MetaName)2575850962, 0, 0, 40,
+ return new PsoStructureInfo((MetaName)2575850962, 0, 0, 44 /*40*/,
new PsoStructureEntryInfo(MetaName.ARRAYINFO, PsoDataType.UByte, 0, 0, 0),
- new PsoStructureEntryInfo(MetaName.indices, PsoDataType.Array, 8, 4, (MetaName)262144),
+ new PsoStructureEntryInfo(MetaName.indices, PsoDataType.Array, 8, 4, (MetaName)393216 /*262144*/),
new PsoStructureEntryInfo(MetaName.ARRAYINFO, PsoDataType.Bool, 0, 0, 0),
- new PsoStructureEntryInfo(MetaName.liveries, PsoDataType.Array, 12, 4, (MetaName)1638402)
+ new PsoStructureEntryInfo(MetaName.liveries, PsoDataType.Array, 14 /*12*/, 4, (MetaName)1966082 /*1638402*/)
);
case (MetaName)938618322:
return new PsoStructureInfo((MetaName)938618322, 0, 0, 16,
@@ -16104,10 +16104,13 @@ namespace CodeWalker.GameFiles
var blocki = (int)ptr.PointerDataId;// (ptr.Pointer & 0xFFF) - 1;
var offset = (int)ptr.PointerDataOffset;// (ptr.Pointer >> 12) & 0xFFFFF;
- var block = pso.GetBlock(blocki);
+ var block = pso.GetBlock(blocki); //nameHash = 1
if (block == null)
{ return null; }
+ //if (block.NameHash != (MetaName)1)
+ //{ }
+
var length = ptr.Count1;
var lastbyte = offset + length;
if (lastbyte >= block.Length)
@@ -16133,10 +16136,13 @@ namespace CodeWalker.GameFiles
var blocki = (int)ptr.PointerDataId;// (ptr.Pointer & 0xFFF) - 1;
var offset = (int)ptr.PointerDataOffset;// (ptr.Pointer >> 12) & 0xFFFFF;
- var block = pso.GetBlock(blocki);
+ var block = pso.GetBlock(blocki); //nameHash = 1
if (block == null)
{ return null; }
+ //if (block.NameHash != (MetaName)1)
+ //{ }
+
//var length = ptr.Count1;
//var lastbyte = offset + length;
//if (lastbyte >= block.Length)
@@ -16217,6 +16223,13 @@ namespace CodeWalker.GameFiles
public uint ItemOffset { get { return ((Pointer>>12) & 0xFFFFF); } } //byte offset
+ public PsoPOINTER(int blockID, int itemOffset, uint extra)
+ {
+ Pointer = (((uint)itemOffset << 12) & 0xFFFFF000) + ((uint)blockID & 0xFFF);
+ Unk2 = extra;
+ }
+
+
public override string ToString()
{
return BlockID.ToString() + ", " + ItemOffset.ToString() + ", " + Unk2.ToString();
diff --git a/CodeWalker.Core/GameFiles/MetaTypes/XmlPso.cs b/CodeWalker.Core/GameFiles/MetaTypes/XmlPso.cs
new file mode 100644
index 0000000..6584027
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/MetaTypes/XmlPso.cs
@@ -0,0 +1,1222 @@
+using SharpDX;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace CodeWalker.GameFiles
+{
+ public class XmlPso
+ {
+
+ public static PsoFile GetPso(XmlDocument doc)
+ {
+ PsoBuilder pb = new PsoBuilder();
+
+ Traverse(doc.DocumentElement, pb, 0, true);
+
+ var pso = pb.GetPso();
+
+ return pso;
+ }
+
+
+
+ private static byte[] Traverse(XmlNode node, PsoBuilder pb, MetaName type = 0, bool isRoot = false)
+ {
+ if (type == 0)
+ {
+ type = (MetaName)(uint)GetHash(node.Name);
+ }
+
+ var infos = PsoTypes.GetStructureInfo(type);
+ if (infos != null)
+ {
+ byte[] data = new byte[infos.StructureLength];
+ var arrayResults = new PsoArrayResults();
+
+ arrayResults.Structures = new Dictionary();
+ arrayResults.StructurePointers = new Dictionary();
+ arrayResults.UInts = new Dictionary();
+ arrayResults.UShorts = new Dictionary();
+ arrayResults.UBytes = new Dictionary();
+ arrayResults.Floats = new Dictionary();
+ arrayResults.Float_XYZs = new Dictionary();
+ arrayResults.Hashes = new Dictionary();
+
+ Array.Clear(data, 0, infos.StructureLength); //shouldn't really be necessary...
+
+ PsoStructureEntryInfo arrEntry = null;
+
+
+ //if (isRoot)
+ //{
+ // pb.EnsureBlock(type);
+ //}
+
+ for (int i = 0; i < infos.Entries.Length; i++)
+ {
+ var entry = infos.Entries[i];
+ var cnode = GetEntryNode(node.ChildNodes, entry.EntryNameHash);
+
+ if (entry.EntryNameHash == MetaName.ARRAYINFO)
+ {
+ arrEntry = entry;
+ continue;
+ }
+
+ if (cnode == null)
+ {
+ //warning: node not found in XML for this entry!
+ continue;
+ }
+
+ switch (entry.Type)
+ {
+ case PsoDataType.Array:
+ {
+ TraverseArray(cnode, pb, entry, arrEntry, arrayResults, data, infos);
+ break;
+ }
+ case PsoDataType.Structure:
+ {
+ var struc = Traverse(cnode, pb, (MetaName)entry.ReferenceKey);
+ if (struc != null)
+ {
+ switch (entry.Unk_5h)
+ {
+ default:
+ //ErrorXml(sb, cind, ename + ": Unexpected Structure subtype: " + entry.Unk_5h.ToString());
+ break;
+ case 0: //default structure
+
+ Buffer.BlockCopy(struc, 0, data, entry.DataOffset, struc.Length);
+
+ break;
+ case 3: //structure pointer...
+ case 4: //also pointer? what's the difference?
+
+ var bptr = pb.AddItem((MetaName)entry.ReferenceKey, struc);
+ var ptr = new PsoPOINTER(bptr.BlockID, bptr.Offset, 0);
+ ptr.SwapEnd();
+ var ptrb = MetaTypes.ConvertToBytes(ptr);
+
+ Buffer.BlockCopy(ptrb, 0, data, entry.DataOffset, ptrb.Length);
+
+ break;
+ }
+ }
+ break;
+ }
+ case PsoDataType.Map:
+ {
+ TraverseMap(cnode, pb, entry, infos, data, arrayResults);
+
+ break;
+ }
+
+ case PsoDataType.Bool:
+ {
+ byte val = (cnode.Attributes["value"].Value == "false") ? (byte)0 : (byte)1;
+ data[entry.DataOffset] = val;
+ break;
+ }
+ case PsoDataType.SByte:
+ {
+ var val = Convert.ToSByte(cnode.Attributes["value"].Value);
+ data[entry.DataOffset] = (byte)val;
+ break;
+ }
+ case PsoDataType.UByte:
+ {
+ var val = Convert.ToByte(cnode.Attributes["value"].Value);
+ data[entry.DataOffset] = val;
+ break;
+ }
+ case PsoDataType.SShort:
+ {
+ var val = Convert.ToInt16(cnode.Attributes["value"].Value);
+ Write(val, data, entry.DataOffset);
+ break;
+ }
+ case PsoDataType.UShort:
+ {
+ var val = Convert.ToUInt16(cnode.Attributes["value"].Value);
+ Write(val, data, entry.DataOffset);
+ break;
+ }
+ case PsoDataType.SInt:
+ {
+ var val = Convert.ToInt32(cnode.Attributes["value"].Value);
+ Write(val, data, entry.DataOffset);
+ 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 (? flags?)
+ var sval = Convert.ToInt32(cnode.Attributes["value"].Value);
+ Write(sval, data, entry.DataOffset);
+ break;
+ case 1: //unsigned int
+ var uval = Convert.ToUInt32(cnode.Attributes["value"].Value);
+ Write(uval, data, entry.DataOffset);
+ break;
+ }
+
+ break;
+ }
+ case PsoDataType.Float:
+ {
+ float val = FloatUtil.Parse(cnode.Attributes["value"].Value);
+ Write(val, data, entry.DataOffset);
+ break;
+ }
+ case PsoDataType.Float2:
+ {
+ float x = FloatUtil.Parse(cnode.Attributes["x"].Value);
+ float y = FloatUtil.Parse(cnode.Attributes["y"].Value);
+ Write(x, data, entry.DataOffset);
+ Write(y, data, entry.DataOffset + sizeof(float));
+ break;
+ }
+ case PsoDataType.Float3:
+ {
+ float x = FloatUtil.Parse(cnode.Attributes["x"].Value);
+ float y = FloatUtil.Parse(cnode.Attributes["y"].Value);
+ float z = FloatUtil.Parse(cnode.Attributes["z"].Value);
+ Write(x, data, entry.DataOffset);
+ Write(y, data, entry.DataOffset + sizeof(float));
+ Write(z, data, entry.DataOffset + sizeof(float) * 2);
+ break;
+ }
+ case PsoDataType.Float4:
+ {
+ float x = FloatUtil.Parse(cnode.Attributes["x"].Value);
+ float y = FloatUtil.Parse(cnode.Attributes["y"].Value);
+ float z = FloatUtil.Parse(cnode.Attributes["z"].Value);
+ float w = FloatUtil.Parse(cnode.Attributes["w"].Value);
+ Write(x, data, entry.DataOffset);
+ Write(y, data, entry.DataOffset + sizeof(float));
+ Write(z, data, entry.DataOffset + sizeof(float) * 2);
+ Write(w, data, entry.DataOffset + sizeof(float) * 3);
+ break;
+ }
+ case PsoDataType.String:
+ {
+ TraverseString(cnode, pb, entry, data);
+ break;
+ }
+ case PsoDataType.Enum:
+ {
+ pb.AddEnumInfo((MetaName)entry.ReferenceKey);
+ switch (entry.Unk_5h)
+ {
+ default:
+ //ErrorXml(sb, cind, ename + ": Unexpected Enum subtype: " + entry.Unk_5h.ToString());
+ break;
+ case 0: //int enum
+ int ival = GetEnumInt((MetaName)entry.ReferenceKey, cnode.InnerText, entry.Type);
+ Write(ival, data, entry.DataOffset);
+ break;
+ case 1: //short enum?
+ short sval = (short)GetEnumInt((MetaName)entry.ReferenceKey, cnode.InnerText, entry.Type);
+ Write(sval, data, entry.DataOffset);
+ break;
+ case 2: //byte enum
+ byte bval = (byte)GetEnumInt((MetaName)entry.ReferenceKey, cnode.InnerText, entry.Type);
+ data[entry.DataOffset] = bval;
+ break;
+ }
+ break;
+ }
+ case PsoDataType.Flags:
+ {
+ //uint fCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
+ uint fEntry = (entry.ReferenceKey & 0xFFF);
+ var fEnt = (fEntry != 0xFFF) ? infos.GetEntry((int)fEntry) : null;
+ PsoEnumInfo flagsInfo = null;
+ MetaName fEnum = (MetaName)(fEnt?.ReferenceKey ?? 0);
+ if ((fEnt != null) && (fEnt.EntryNameHash == MetaName.ARRAYINFO))
+ {
+ flagsInfo = PsoTypes.GetEnumInfo(fEnum);
+ }
+ if (flagsInfo == null)
+ {
+ if (fEntry != 0xFFF)
+ { }
+ //flagsInfo = PsoTypes.GetEnumInfo(entry.EntryNameHash);
+ }
+ if (flagsInfo != null)
+ {
+ pb.AddEnumInfo(flagsInfo.IndexInfo.NameHash);
+ }
+ else
+ { }//error?
+
+ switch (entry.Unk_5h)
+ {
+ default:
+ //ErrorXml(sb, cind, ename + ": Unexpected Flags subtype: " + entry.Unk_5h.ToString());
+ break;
+ case 0: //int flags
+ int ival = GetEnumInt(fEnum, cnode.InnerText, entry.Type);
+ Write(ival, data, entry.DataOffset);
+ break;
+ case 1: //short flags
+ short sval = (short)GetEnumInt(fEnum, cnode.InnerText, entry.Type);
+ Write(sval, data, entry.DataOffset);
+ break;
+ case 2: //byte flags
+ byte bval = (byte)GetEnumInt(fEnum, cnode.InnerText, entry.Type);
+ data[entry.DataOffset] = bval;
+ break;
+ }
+ break;
+ }
+ case PsoDataType.Float3a:
+ {
+ float x = FloatUtil.Parse(cnode.Attributes["x"].Value);
+ float y = FloatUtil.Parse(cnode.Attributes["y"].Value);
+ float z = FloatUtil.Parse(cnode.Attributes["z"].Value);
+ Write(x, data, entry.DataOffset);
+ Write(y, data, entry.DataOffset + sizeof(float));
+ Write(z, data, entry.DataOffset + sizeof(float) * 2);
+ break;
+ }
+ case PsoDataType.Float4a:
+ {
+ float x = FloatUtil.Parse(cnode.Attributes["x"].Value);
+ float y = FloatUtil.Parse(cnode.Attributes["y"].Value);
+ float z = FloatUtil.Parse(cnode.Attributes["z"].Value);
+ //float w = FloatUtil.Parse(cnode.Attributes["w"].Value);
+ Write(x, data, entry.DataOffset);
+ Write(y, data, entry.DataOffset + sizeof(float));
+ Write(z, data, entry.DataOffset + sizeof(float) * 2);
+ //Write(w, data, entry.DataOffset + sizeof(float) * 3);
+ break;
+ }
+ case PsoDataType.HFloat:
+ {
+ var val = Convert.ToInt16(cnode.Attributes["value"].Value);
+ Write(val, data, entry.DataOffset);
+ break;
+ }
+ case PsoDataType.Long:
+ {
+ var uval = Convert.ToUInt64(cnode.Attributes["value"].Value);
+ Write(uval, data, entry.DataOffset);
+ break;
+ }
+
+
+ default:
+ break;
+
+ }
+
+
+ }
+
+
+ arrayResults.WriteArrays(data);
+
+ pb.AddStructureInfo(infos.IndexInfo.NameHash);
+
+ if (isRoot)
+ {
+ pb.RootPointer = pb.AddItem(type, data);
+ }
+
+ return data;
+
+ }
+
+ return null;
+ }
+
+ private static void TraverseMap(XmlNode node, PsoBuilder pb, PsoStructureEntryInfo entry, PsoStructureInfo infos, byte[] data, PsoArrayResults arrayResults)
+ {
+ var mapidx1 = entry.ReferenceKey & 0x0000FFFF;
+ var mapidx2 = (entry.ReferenceKey >> 16) & 0x0000FFFF;
+ var mapreftype1 = infos.Entries[mapidx2];
+ var mapreftype2 = infos.Entries[mapidx1];
+
+ if (mapreftype2.ReferenceKey != 0)
+ { }
+
+ var xStruct = pb.AddMapNodeStructureInfo((MetaName)mapreftype2.ReferenceKey);
+ var xName = xStruct.IndexInfo.NameHash;
+ var kEntry = xStruct?.FindEntry(MetaName.Key);
+ var iEntry = xStruct?.FindEntry(MetaName.Item);
+
+ if (kEntry.Type != PsoDataType.String)
+ { }
+
+
+
+ List nodesData = new List();
+
+ foreach (XmlNode cnode in node.ChildNodes)
+ {
+ var kattr = cnode.Attributes["key"].Value;
+ var tattr = cnode.Attributes["type"].Value;//CW invention for convenience..!
+ var khash = (MetaName)(uint)GetHash(kattr);
+ var thash = (MetaName)(uint)GetHash(tattr);
+
+ byte[] strucBytes = Traverse(cnode, pb, thash);
+ byte[] nodeBytes = new byte[xStruct.StructureLength];
+
+ TraverseStringRaw(kattr, pb, kEntry, nodeBytes); //write the key
+
+ if (xName != MetaName.ARRAYINFO)// (mapreftype2.ReferenceKey != 0)
+ {
+ //value struct embedded in ARRAYINFO node
+ Buffer.BlockCopy(strucBytes, 0, nodeBytes, iEntry.DataOffset, strucBytes.Length);
+ }
+ else
+ {
+ //normal ARRAYINFO with pointer value
+ var itemptr = pb.AddItemPtr(thash, strucBytes);
+ itemptr.SwapEnd(); //big schmigg
+ var ptrbytes = MetaTypes.ConvertToBytes(itemptr);
+ Buffer.BlockCopy(ptrbytes, 0, nodeBytes, iEntry.DataOffset, ptrbytes.Length);
+ }
+
+ nodesData.Add(nodeBytes);
+
+ }
+
+
+
+ Write(0x1000000, data, entry.DataOffset);
+ Write(0, data, entry.DataOffset + 4);
+
+ arrayResults.Structures[entry.DataOffset + 8] = pb.AddItemArrayPtr(xName, nodesData.ToArray()); //pb.AddPointerArray(nodeptrs);
+ }
+
+ private static void TraverseArray(XmlNode node, PsoBuilder pb, PsoStructureEntryInfo entry, PsoStructureEntryInfo arrEntry, PsoArrayResults results, byte[] data, PsoStructureInfo structInfo)
+ {
+ int offset = entry.DataOffset;
+ uint aCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
+ uint aPtr = (entry.ReferenceKey) & 0x0000FFFF;
+ byte[] adata = null;
+
+ //how do we know when it's an "embedded" array?
+ bool embedded = true;
+ 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(data, eoffset);
+ embedded = false;
+ 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(data, eoffset);
+ embedded = false;
+ }
+ else
+ {
+ }
+ break;
+ case 129: //also raw inline array? in junctions.pso (AutoJunctionAdjustments)
+ break;
+ }
+
+
+
+
+ switch (arrEntry.Type)
+ {
+ case PsoDataType.Structure:
+ {
+ if (embedded)
+ {
+ if (arrEntry.ReferenceKey != 0)
+ {
+ var datas = TraverseArrayStructureRaw(node, pb, (MetaName)arrEntry.ReferenceKey);
+ int aoffset = offset;
+ for (int i = 0; i < datas.Length; i++)
+ {
+ Buffer.BlockCopy(datas[i], 0, data, aoffset, datas[i].Length);
+ aoffset += datas[i].Length;
+ }
+ }
+ else
+ {
+ var ptrs = TraverseArrayStructurePointerRaw(node, pb);
+ adata = MetaTypes.ConvertArrayToBytes(ptrs);
+ }
+ }
+ else
+ {
+ if (arrEntry.ReferenceKey != 0)
+ {
+ results.Structures[offset] = TraverseArrayStructure(node, pb, (MetaName)arrEntry.ReferenceKey);
+ }
+ else
+ {
+ results.StructurePointers[offset] = TraverseArrayStructurePointer(node, pb);
+ }
+ }
+ break;
+ }
+
+ case PsoDataType.Float2:
+ {
+ var arr = TraverseVector2ArrayRaw(node);
+ if (embedded)
+ {
+ adata = MetaTypes.ConvertArrayToBytes(arr);
+ aCount *= 8;
+ }
+ else
+ {
+ results.Float_XYZs[offset] = pb.AddVector2ArrayPtr(arr);
+ }
+ break;
+ }
+ case PsoDataType.Float3:
+ {
+ var arr = TraverseVector3ArrayRaw(node);
+ if (embedded)
+ {
+ adata = MetaTypes.ConvertArrayToBytes(arr);
+ aCount *= 16;
+ }
+ else
+ {
+ results.Float_XYZs[offset] = pb.AddPaddedVector3ArrayPtr(arr);
+ }
+ break;
+ }
+ case PsoDataType.UByte:
+ {
+ var arr = TraverseUByteArrayRaw(node);
+ if (embedded)
+ {
+ adata = arr;
+ }
+ else
+ {
+ results.UBytes[offset] = pb.AddByteArrayPtr(arr);
+ }
+ break;
+ }
+ case PsoDataType.Bool:
+ {
+ var arr = TraverseUByteArrayRaw(node);
+ if (embedded)
+ {
+ adata = arr;
+ }
+ else
+ {
+ results.UBytes[offset] = pb.AddByteArrayPtr(arr);
+ }
+ break;
+ }
+ case PsoDataType.UInt:
+ {
+ var arr = TraverseUIntArrayRaw(node);
+ if (embedded)
+ {
+ adata = MetaTypes.ConvertArrayToBytes(arr);
+ aCount *= 4;
+ }
+ else
+ {
+ results.UInts[offset] = pb.AddUIntArrayPtr(arr);
+ }
+ break;
+ }
+ case PsoDataType.SInt:
+ {
+ var arr = TraverseSIntArrayRaw(node);
+ if (embedded)
+ {
+ adata = MetaTypes.ConvertArrayToBytes(arr);
+ aCount *= 4;
+ }
+ else
+ {
+ results.UInts[offset] = pb.AddSIntArrayPtr(arr);
+ }
+ break;
+ }
+ case PsoDataType.Float:
+ {
+ var arr = TraverseFloatArrayRaw(node);
+ if (embedded)
+ {
+ adata = MetaTypes.ConvertArrayToBytes(arr);
+ aCount *= 4;
+ }
+ else
+ {
+ results.Floats[offset] = pb.AddFloatArrayPtr(arr);
+ }
+ break;
+ }
+ case PsoDataType.UShort:
+ {
+ var arr = TraverseUShortArrayRaw(node);
+ if (embedded)
+ {
+ adata = MetaTypes.ConvertArrayToBytes(arr);
+ aCount *= 2;
+ }
+ else
+ {
+ results.UShorts[offset] = pb.AddUShortArrayPtr(arr);
+ }
+ 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 hashes = TraverseHashArrayRaw(node);
+ if (embedded)
+ {
+ adata = MetaTypes.ConvertArrayToBytes(hashes);
+ aCount *= 4;
+ }
+ else
+ {
+ results.Hashes[offset] = pb.AddHashArrayPtr(hashes);
+ }
+ break;
+ }
+
+
+ break;
+ }
+
+
+ case PsoDataType.Enum:
+ {
+ var hashes = TraverseHashArrayRaw(node);
+
+ if (arrEntry.ReferenceKey != 0)
+ {
+ var _infos = PsoTypes.GetEnumInfo((MetaName)arrEntry.ReferenceKey);
+ pb.AddEnumInfo(_infos.IndexInfo.NameHash);
+
+ var values = new uint[hashes.Length];
+ for (int i = 0; i < hashes.Length; i++)
+ {
+ var enumname = (MetaName)MetaTypes.SwapBytes(hashes[i]);//yeah swap it back to little endian..!
+ var enuminf = _infos.FindEntryByName(enumname);
+ if (enuminf != null)
+ {
+ values[i] = MetaTypes.SwapBytes((uint)enuminf.EntryKey);
+ }
+ else
+ { } //error?
+ }
+
+ if (embedded)
+ { } //TODO?
+ else
+ {
+ results.UInts[offset] = pb.AddUIntArrayPtr(values);
+ }
+
+ }
+ else
+ { }
+
+
+ break;
+ }
+
+
+ case PsoDataType.Array:
+ {
+ //array array...
+ var rk0 = (entry.ReferenceKey >> 16) & 0x0000FFFF;
+ var rk1 = arrEntry.ReferenceKey & 0x0000FFFF;
+ if (rk0 > 0) //should be count of items
+ {
+ var subarrEntry = structInfo.GetEntry((int)rk1);
+ var subarrType = (MetaName)subarrEntry.ReferenceKey;
+
+ var origOffset = arrEntry.DataOffset;
+ arrEntry.DataOffset = entry.DataOffset;//slight hack for traversing array array
+ foreach (XmlNode cnode in node.ChildNodes)
+ {
+ TraverseArray(cnode, pb, arrEntry, subarrEntry, results, data, structInfo);
+
+ arrEntry.DataOffset += 16;//ptr size... todo: what if not pointer array?
+ }
+ arrEntry.DataOffset = origOffset;
+
+
+ }
+
+
+ break;
+ }
+
+
+
+
+ default:
+ break;
+ }
+
+ if (embedded)
+ {
+ if (adata?.Length > 0)
+ {
+ if (adata.Length > aCount)
+ { }//bad! old data won't fit in new slot...
+
+ Buffer.BlockCopy(adata, 0, data, offset, adata.Length);
+ }
+ else
+ { }
+ }
+ }
+
+ private static void TraverseString(XmlNode node, PsoBuilder pb, PsoStructureEntryInfo entry, byte[] data)
+ {
+ TraverseStringRaw(node.InnerText, pb, entry, data);
+ }
+ private static void TraverseStringRaw(string str, PsoBuilder pb, PsoStructureEntryInfo entry, byte[] data)
+ {
+ switch (entry.Unk_5h)
+ {
+ default:
+ break;
+ case 0:
+ var str0len = (int)((entry.ReferenceKey >> 16) & 0xFFFF);
+ if (!string.IsNullOrEmpty(str))
+ {
+ byte[] strdata = Encoding.ASCII.GetBytes(str);
+ Buffer.BlockCopy(strdata, 0, data, entry.DataOffset, strdata.Length);
+ if (strdata.Length > str0len)
+ { }
+ }
+ break;
+ case 1:
+ case 2:
+ if (!string.IsNullOrEmpty(str))
+ {
+ var bptr = pb.AddString(str);
+ var ptr = new PsoPOINTER(bptr.BlockID, bptr.Offset, 0);
+ ptr.SwapEnd();
+ var val = MetaTypes.ConvertToBytes(ptr);
+ Buffer.BlockCopy(val, 0, data, entry.DataOffset, val.Length);
+ }
+ break;
+ case 3:
+ if (!string.IsNullOrEmpty(str))
+ {
+ var bptr = pb.AddString(str);
+ var ptr = new CharPointer(bptr.Pointer, str.Length);
+ ptr.SwapEnd();
+ var val = MetaTypes.ConvertToBytes(ptr);
+ Buffer.BlockCopy(val, 0, data, entry.DataOffset, val.Length);
+ }
+ break;
+ case 7://hash only?
+ case 8://hash with STRF entry?
+
+ var hashVal = string.IsNullOrEmpty(str) ? 0 : GetHash(str);
+ Write(hashVal, data, entry.DataOffset);
+
+ if (entry.Unk_5h == 8)
+ {
+ pb.AddStringToSTRF(str);
+ }
+ break;
+ }
+ }
+
+
+ private static byte[][] TraverseArrayStructureRaw(XmlNode node, PsoBuilder pb, MetaName type)
+ {
+ var strucs = new List();
+
+ foreach (XmlNode cnode in node.ChildNodes)
+ {
+ var struc = Traverse(cnode, pb, type);
+
+ if (struc != null)
+ {
+ strucs.Add(struc);
+ }
+ }
+
+ return strucs.ToArray();
+ }
+ private static Array_Structure TraverseArrayStructure(XmlNode node, PsoBuilder pb, MetaName type)
+ {
+ var bytes = TraverseArrayStructureRaw(node, pb, type);
+
+ return pb.AddItemArrayPtr(type, bytes);
+ }
+
+ private static PsoPOINTER[] TraverseArrayStructurePointerRaw(XmlNode node, PsoBuilder pb)
+ {
+ var ptrs = new List();
+
+ foreach (XmlNode cnode in node.ChildNodes)
+ {
+ var type = (MetaName)(uint)GetHash(cnode.Attributes["type"]?.Value ?? "");
+ if (type != 0)
+ {
+ var struc = Traverse(cnode, pb, type);
+
+ if (struc != null)
+ {
+ var ptr = pb.AddItemPtr(type, struc);
+ ptr.SwapEnd(); //big schmigg
+ ptrs.Add(ptr);
+ }
+ else
+ { } //error?
+ }
+ else
+ {
+ ptrs.Add(new PsoPOINTER());//null value?
+ }
+ }
+
+ return ptrs.ToArray();
+ }
+ private static Array_StructurePointer TraverseArrayStructurePointer(XmlNode node, PsoBuilder pb)
+ {
+ var ptrs = TraverseArrayStructurePointerRaw(node, pb);
+
+ return pb.AddPointerArray(ptrs);
+
+ }
+
+ private static int[] TraverseSIntArrayRaw(XmlNode node)
+ {
+ var data = new List();
+
+ if (node.InnerText != "")
+ {
+ var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ if (!string.IsNullOrEmpty(split[i]))
+ {
+ var val = Convert.ToInt32(split[i]);
+ data.Add(MetaTypes.SwapBytes(val));
+ }
+
+ }
+ }
+
+ return data.ToArray();
+ }
+ private static uint[] TraverseUIntArrayRaw(XmlNode node)
+ {
+ var data = new List();
+
+ if (node.InnerText != "")
+ {
+ var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ if (!string.IsNullOrEmpty(split[i]))
+ {
+ var val = Convert.ToUInt32(split[i]);
+ data.Add(MetaTypes.SwapBytes(val));
+ }
+
+ }
+ }
+
+ return data.ToArray();
+ }
+ private static byte[] TraverseUByteArrayRaw(XmlNode node)
+ {
+ var data = new List();
+
+ if (node.InnerText != "")
+ {
+ var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ if (!string.IsNullOrEmpty(split[i]))
+ {
+ var val = Convert.ToByte(split[i]);
+ data.Add(val);
+ }
+ }
+ }
+
+ return data.ToArray();
+ }
+ private static ushort[] TraverseUShortArrayRaw(XmlNode node)
+ {
+ var data = new List();
+
+ if (node.InnerText != "")
+ {
+ var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ if (!string.IsNullOrEmpty(split[i]))
+ {
+ var val = Convert.ToUInt16(split[i]);
+ data.Add(MetaTypes.SwapBytes(val));
+ }
+
+ }
+ }
+
+ return data.ToArray();
+ }
+ private static float[] TraverseFloatArrayRaw(XmlNode node)
+ {
+ var data = new List();
+
+ if (node.InnerText != "")
+ {
+ var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ if (!string.IsNullOrEmpty(split[i]))
+ {
+ var val = FloatUtil.Parse(split[i]);
+ data.Add(MetaTypes.SwapBytes(val));
+ }
+
+ }
+ }
+
+ return data.ToArray();
+ }
+ private static Vector4[] TraverseVector3ArrayRaw(XmlNode node)
+ {
+ var items = new List();
+
+
+ var split = node.InnerText.Split('\n');// Regex.Split(node.InnerText, @"[\s\r\n\t]");
+
+
+ float x = 0f;
+ float y = 0f;
+ float z = 0f;
+ float w = 0f;
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ var s = split[i]?.Trim();
+ if (string.IsNullOrEmpty(s)) continue;
+ var split2 = Regex.Split(s, @"[\s\t]");
+ int c = 0;
+ x = 0f; y = 0f; z = 0f;
+ for (int n = 0; n < split2.Length; n++)
+ {
+ var ts = split2[n]?.Trim();
+ if (ts.EndsWith(",")) ts = ts.Substring(0, ts.Length - 1);
+ if (string.IsNullOrEmpty(ts)) continue;
+ var f = FloatUtil.Parse(ts);
+ switch (c)
+ {
+ case 0: x = f; break;
+ case 1: y = f; break;
+ case 2: z = f; break;
+ }
+ c++;
+ }
+ if (c >= 3)
+ {
+ var val = new Vector4(x, y, z, w);
+ items.Add(MetaTypes.SwapBytes(val)); //big schmig
+ }
+ }
+
+
+ return items.ToArray();
+ }
+ private static Vector2[] TraverseVector2ArrayRaw(XmlNode node)
+ {
+ var items = new List();
+
+
+ var split = node.InnerText.Split('\n');// Regex.Split(node.InnerText, @"[\s\r\n\t]");
+
+
+ float x = 0f;
+ float y = 0f;
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ var s = split[i]?.Trim();
+ if (string.IsNullOrEmpty(s)) continue;
+ var split2 = Regex.Split(s, @"[\s\t]");
+ int c = 0;
+ x = 0f; y = 0f;
+ for (int n = 0; n < split2.Length; n++)
+ {
+ var ts = split2[n]?.Trim();
+ if (ts.EndsWith(",")) ts = ts.Substring(0, ts.Length - 1);
+ if (string.IsNullOrEmpty(ts)) continue;
+ var f = FloatUtil.Parse(ts);
+ switch (c)
+ {
+ case 0: x = f; break;
+ case 1: y = f; break;
+ }
+ c++;
+ }
+ if (c >= 3)
+ {
+ var val = new Vector2(x, y);
+ items.Add(MetaTypes.SwapBytes(val)); //big schmig
+ }
+ }
+
+
+ return items.ToArray();
+ }
+ private static MetaHash[] TraverseHashArrayRaw(XmlNode node)
+ {
+ var items = new List();
+
+ foreach (XmlNode cnode in node.ChildNodes)
+ {
+ var val = GetHash(cnode.InnerText);
+ items.Add(MetaTypes.SwapBytes(val));
+ }
+
+ return items.ToArray();
+ }
+
+
+
+
+ private static void Write(int val, byte[] data, int offset)
+ {
+ byte[] bytes = BitConverter.GetBytes(MetaTypes.SwapBytes(val));
+ Buffer.BlockCopy(bytes, 0, data, offset, sizeof(int));
+ }
+
+ private static void Write(uint val, byte[] data, int offset)
+ {
+ byte[] bytes = BitConverter.GetBytes(MetaTypes.SwapBytes(val));
+ Buffer.BlockCopy(bytes, 0, data, offset, sizeof(uint));
+ }
+
+ private static void Write(short val, byte[] data, int offset)
+ {
+ byte[] bytes = BitConverter.GetBytes(MetaTypes.SwapBytes(val));
+ Buffer.BlockCopy(bytes, 0, data, offset, sizeof(short));
+ }
+
+ private static void Write(ushort val, byte[] data, int offset)
+ {
+ byte[] bytes = BitConverter.GetBytes(MetaTypes.SwapBytes(val));
+ Buffer.BlockCopy(bytes, 0, data, offset, sizeof(ushort));
+ }
+
+ private static void Write(float val, byte[] data, int offset)
+ {
+ byte[] bytes = BitConverter.GetBytes(MetaTypes.SwapBytes(val));//big fkn end
+ Buffer.BlockCopy(bytes, 0, data, offset, sizeof(float));
+ }
+
+ private static void Write(ulong val, byte[] data, int offset)
+ {
+ byte[] bytes = BitConverter.GetBytes(MetaTypes.SwapBytes(val));
+ Buffer.BlockCopy(bytes, 0, data, offset, sizeof(ulong));
+ }
+
+ private static MetaHash GetHash(string str)
+ {
+ if (str.StartsWith("hash_"))
+ {
+ return (MetaHash)Convert.ToUInt32(str.Substring(5), 16);
+ }
+ else
+ {
+ return JenkHash.GenHash(str);
+ }
+ }
+
+ private static XmlNode GetEntryNode(XmlNodeList nodes, MetaName name)
+ {
+ foreach (XmlNode node in nodes)
+ {
+ if (GetHash(node.Name) == (uint)name)
+ {
+ return node;
+ }
+ }
+ return null;
+ }
+
+
+ private static int GetEnumInt(MetaName type, string enumString, PsoDataType dataType)
+ {
+ var infos = PsoTypes.GetEnumInfo(type);
+
+ if (infos == null)
+ {
+ return 0;
+ }
+
+
+ bool isFlags = (dataType == PsoDataType.Flags);// ||
+ //(dataType == PsoDataType.IntFlags2);// ||
+ //(dataType == PsoDataType.ShortFlags);
+
+ if (isFlags)
+ {
+ //flags enum. (multiple values, comma-separated)
+ var split = enumString.Split(new[] { ',' ,' '}, StringSplitOptions.RemoveEmptyEntries);
+ int enumVal = 0;
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ var enumName = (MetaName)(uint)GetHash(split[i].Trim());
+
+ for (int j = 0; j < infos.Entries.Length; j++)
+ {
+ var entry = infos.Entries[j];
+ if (entry.EntryNameHash == enumName)
+ {
+ enumVal += (1 << entry.EntryKey);
+ break;
+ }
+ }
+ }
+
+ return enumVal;
+ }
+ else
+ {
+ //single value enum.
+
+ var enumName = (MetaName)(uint)GetHash(enumString);
+
+ for (int j = 0; j < infos.Entries.Length; j++)
+ {
+ var entry = infos.Entries[j];
+
+ if (entry.EntryNameHash == enumName)
+ {
+ return entry.EntryKey;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ }
+
+ struct PsoArrayResults
+ {
+ public Dictionary Structures;
+ public Dictionary StructurePointers;
+ public Dictionary UInts;
+ public Dictionary UShorts;
+ public Dictionary UBytes;
+ public Dictionary Floats;
+ public Dictionary Float_XYZs;
+ public Dictionary Hashes;
+
+ public void WriteArrays(byte[] data)
+ {
+ foreach (KeyValuePair ptr in Structures)
+ {
+ var val = ptr.Value;
+ val.SwapEnd();
+ var _data = MetaTypes.ConvertToBytes(val);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in StructurePointers)
+ {
+ var val = ptr.Value;
+ val.SwapEnd();
+ var _data = MetaTypes.ConvertToBytes(val);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in UInts)
+ {
+ var val = ptr.Value;
+ val.SwapEnd();
+ var _data = MetaTypes.ConvertToBytes(val);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in UShorts)
+ {
+ var val = ptr.Value;
+ val.SwapEnd();
+ var _data = MetaTypes.ConvertToBytes(val);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in UBytes)
+ {
+ var val = ptr.Value;
+ val.SwapEnd();
+ var _data = MetaTypes.ConvertToBytes(val);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in Floats)
+ {
+ var val = ptr.Value;
+ val.SwapEnd();
+ var _data = MetaTypes.ConvertToBytes(val);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in Float_XYZs)
+ {
+ var val = ptr.Value;
+ val.SwapEnd();
+ var _data = MetaTypes.ConvertToBytes(val);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in Hashes)
+ {
+ var val = ptr.Value;
+ val.SwapEnd();
+ var _data = MetaTypes.ConvertToBytes(val);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+ }
+ }
+
+}
diff --git a/ExploreForm.cs b/ExploreForm.cs
index 1fc911e..efe0d4c 100644
--- a/ExploreForm.cs
+++ b/ExploreForm.cs
@@ -2251,6 +2251,8 @@ namespace CodeWalker
var fi = new FileInfo(fpath);
var fname = fi.Name;
var fnamel = fname.ToLowerInvariant();
+ var mformat = MetaFormat.RSC;
+ var trimlength = 4;
if (!fnamel.EndsWith(".xml"))
{
@@ -2259,8 +2261,8 @@ namespace CodeWalker
}
if (fnamel.EndsWith(".pso.xml"))
{
- MessageBox.Show(fname + ": PSO XML import not yet supported.", "Cannot import XML");
- continue;
+ mformat = MetaFormat.PSO;
+ trimlength = 8;
}
if (fnamel.EndsWith(".rbf.xml"))
{
@@ -2268,8 +2270,8 @@ namespace CodeWalker
continue;
}
- fname = fname.Substring(0, fname.Length - 4);
- fnamel = fnamel.Substring(0, fnamel.Length - 4);
+ fname = fname.Substring(0, fname.Length - trimlength);
+ fnamel = fnamel.Substring(0, fnamel.Length - trimlength);
var doc = new XmlDocument();
string text = File.ReadAllText(fpath);
@@ -2278,21 +2280,46 @@ namespace CodeWalker
doc.LoadXml(text);
}
- var meta = XmlMeta.GetMeta(doc);
+ byte[] data = null;
-
- if ((meta.DataBlocks?.Data == null) || (meta.DataBlocks.Count == 0))
+ switch (mformat)
{
- MessageBox.Show(fname + ": Schema not supported.", "Cannot import XML");
- continue;
+ case MetaFormat.RSC:
+ {
+ var meta = XmlMeta.GetMeta(doc);
+ if ((meta.DataBlocks?.Data == null) || (meta.DataBlocks.Count == 0))
+ {
+ MessageBox.Show(fname + ": Schema not supported.", "Cannot import Meta XML");
+ continue;
+ }
+ data = ResourceBuilder.Build(meta, 2); //meta is RSC V:2
+ break;
+ }
+ case MetaFormat.PSO:
+ {
+ var pso = XmlPso.GetPso(doc);
+ if ((pso.DataSection == null) || (pso.DataMapSection == null) || (pso.SchemaSection == null))
+ {
+ MessageBox.Show(fname + ": Schema not supported.", "Cannot import PSO XML");
+ continue;
+ }
+ data = pso.Save();
+ break;
+ }
+ case MetaFormat.RBF:
+ {
+ //todo!
+ break;
+ }
}
- byte[] data = ResourceBuilder.Build(meta, 2); //meta is RSC V:2
+ if (data != null)
+ {
+ RpfFile.CreateFile(parentrpffldr, fname, data);
+ }
- RpfFile.CreateFile(parentrpffldr, fname, data);
-
}
#if !DEBUG
catch (Exception ex)
diff --git a/Forms/MetaForm.cs b/Forms/MetaForm.cs
index 3076493..bc8db25 100644
--- a/Forms/MetaForm.cs
+++ b/Forms/MetaForm.cs
@@ -350,8 +350,14 @@ namespace CodeWalker.Forms
data = ResourceBuilder.Build(meta, 2); //meta is RSC "Version":2 (it's actually a type identifier, not a version!)
break;
case MetaFormat.PSO:
- MessageBox.Show("Sorry, PSO import is not supported yet.", "Cannot import PSO XML");
- return false;
+ var pso = XmlPso.GetPso(doc);
+ if ((pso.DataSection == null) || (pso.DataMapSection == null) || (pso.SchemaSection == null))
+ {
+ MessageBox.Show("Schema not supported.", "Cannot import PSO XML");
+ return false;
+ }
+ data = pso.Save();
+ break;
case MetaFormat.RBF:
MessageBox.Show("Sorry, RBF import is not supported.", "Cannot import RBF XML");
return false;