private static void WriteNode(StringBuilder sb, int indent, MetaCont cont, int blockId, int offset, XmlTagMode tagMode = XmlTagMode.None, MetaName structName = 0, string metaName = "") { var block = cont.Meta.GetBlock(blockId); if (block == null) { ErrorXml(sb, indent, "Couldn't find block " + blockId + "!"); return; } if (structName == 0) { structName = (MetaName)block.StructureNameHash; } var name = HashString(structName); var data = Array.ConvertAll(block.Data.Data.ToArray(), e => (byte)e); 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, true, metaName); break; case XmlTagMode.Item: OpenTag(sb, indent, "Item", true, metaName); break; case XmlTagMode.ItemAndType: OpenTag(sb, indent, "Item type=\"" + name + "\"", true, metaName); break; } var cind = indent + 1; StructureEntryInfo arrEntry = new StructureEntryInfo(); for (int i = 0; i < structInfo.Entries.Count; i++) { var entry = structInfo.Entries[i]; if ((MetaName)entry.EntryNameHash == MetaName.ARRAYINFO) { arrEntry = entry; continue; } var ename = HashString((MetaName)entry.EntryNameHash); var eoffset = offset + entry.DataOffset; switch (entry.DataType) { default: ErrorXml(sb, cind, ename + ": Unexpected entry DataType: " + entry.DataType.ToString()); break; case StructureEntryDataType.Array: WriteArrayNode(sb, cind, cont, data, arrEntry, ename, eoffset); break; case StructureEntryDataType.ArrayOfBytes: WriteParsedArrayOfBytesNode(sb, cind, data, ename, eoffset, entry, arrEntry); break; case StructureEntryDataType.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 StructureEntryDataType.Boolean: var boolVal = BitConverter.ToBoolean(data, eoffset); ValueTag(sb, cind, ename, boolVal?"true":"false"); break; case StructureEntryDataType.ByteEnum: var byteEnumVal = data[eoffset]; ValueTag(sb, cind, ename, byteEnumVal.ToString()); break; case StructureEntryDataType.CharPointer: var charPtr = MetaUtils.ConvertData <CharPointer>(data, eoffset); string charStr = MetaUtils.GetString(cont.Meta, charPtr); OneLineTag(sb, cind, ename, charStr); break; case StructureEntryDataType.DataBlockPointer: OpenTag(sb, cind, ename); var dataPtr = MetaUtils.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 StructureEntryDataType.Float: var floatVal = BitConverter.ToSingle(data, eoffset); ValueTag(sb, cind, ename, floatVal.ToString(CultureInfo.InvariantCulture)); break; case StructureEntryDataType.Float_XYZ: var v3 = MetaUtils.ConvertData <Vector3>(data, eoffset); SelfClosingTag(sb, cind, ename + " x=\"" + v3.X.ToString(CultureInfo.InvariantCulture) + "\" y=\"" + v3.Y.ToString(CultureInfo.InvariantCulture) + "\" z=\"" + v3.Z.ToString(CultureInfo.InvariantCulture) + "\""); break; case StructureEntryDataType.Float_XYZW: var v4 = MetaUtils.ConvertData <Vector4>(data, eoffset); SelfClosingTag(sb, cind, ename + " x=\"" + v4.X.ToString(CultureInfo.InvariantCulture) + "\" y=\"" + v4.Y.ToString(CultureInfo.InvariantCulture) + "\" z=\"" + v4.Z.ToString(CultureInfo.InvariantCulture) + "\" w=\"" + v4.W.ToString(CultureInfo.InvariantCulture) + "\""); break; case StructureEntryDataType.Hash: var hashVal = (MetaName)MetaUtils.ConvertData <uint>(data, eoffset); var hashStr = HashString(hashVal); StringTag(sb, cind, ename, hashStr); break; case StructureEntryDataType.IntEnum: var intEnumVal = BitConverter.ToInt32(data, eoffset); var intEnumStr = GetEnumString(cont, entry, intEnumVal); StringTag(sb, cind, ename, intEnumStr); break; case StructureEntryDataType.IntFlags1: var intFlags1Val = BitConverter.ToInt32(data, eoffset); var intFlags1Str = GetEnumString(cont, entry, intFlags1Val); StringTag(sb, cind, ename, intFlags1Str); break; case StructureEntryDataType.IntFlags2: var intFlags2Val = BitConverter.ToInt32(data, eoffset); var intFlags2Str = GetEnumString(cont, entry, intFlags2Val); StringTag(sb, cind, ename, intFlags2Str); break; case StructureEntryDataType.ShortFlags: var shortFlagsVal = BitConverter.ToInt16(data, eoffset); var shortFlagsStr = shortFlagsVal.ToString(); // GetEnumString(cont, entry, shortFlagsVal); StringTag(sb, cind, ename, shortFlagsStr); break; case StructureEntryDataType.SignedByte: sbyte sbyteVal = (sbyte)data[eoffset]; ValueTag(sb, cind, ename, sbyteVal.ToString()); break; case StructureEntryDataType.SignedInt: var intVal = BitConverter.ToInt32(data, eoffset); ValueTag(sb, cind, ename, intVal.ToString()); break; case StructureEntryDataType.SignedShort: var shortVal = BitConverter.ToInt16(data, eoffset); ValueTag(sb, cind, ename, shortVal.ToString()); break; case StructureEntryDataType.Structure: OpenTag(sb, cind, ename); WriteNode(sb, cind, cont, blockId, eoffset, XmlTagMode.None, (MetaName)entry.ReferenceKey); CloseTag(sb, cind, ename); break; case StructureEntryDataType.StructurePointer: OpenTag(sb, cind, ename); ErrorXml(sb, cind + 1, "StructurePointer not supported here! Tell dexy!"); CloseTag(sb, cind, ename); break; case StructureEntryDataType.UnsignedByte: var byteVal = data[eoffset]; ValueTag(sb, cind, ename, byteVal.ToString()); //ValueTag(sb, cind, ename, "0x" + byteVal.ToString("X").PadLeft(2, '0')); break; case StructureEntryDataType.UnsignedInt: var uintVal = BitConverter.ToUInt32(data, eoffset); switch ((MetaName)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 StructureEntryDataType.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 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 = (MetaName)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.Count; i++) { var entry = structInfo.Entries[i]; if ((MetaName)entry.EntryNameHash == MetaName.ARRAYINFO) { continue; } var ename = HashString((MetaName)entry.EntryNameHash); var eoffset = boffset + entry.DataOffset; switch (entry.Type) { default: ErrorXml(sb, cind, ename + ": Unexpected entry DataType: " + entry.Type.ToString()); break; case RageLib.GTA5.PSO.DataType.Array: WriteArrayNode(sb, cind, cont, blockId, offset, entry, structInfo, ename); break; case RageLib.GTA5.PSO.DataType.Bool: var boolVal = BitConverter.ToBoolean(data, eoffset); ValueTag(sb, cind, ename, boolVal ? "true" : "false"); break; case RageLib.GTA5.PSO.DataType.SByte: //was LONG_01h //signed byte? //var long1Val = MetaUtils.SwapBytes(BitConverter.ToUInt64(data, eoffset)); //ValueTag(sb, cind, ename, long1Val.ToString()); var byte1Val = (sbyte)data[eoffset]; ValueTag(sb, cind, ename, byte1Val.ToString()); break; case RageLib.GTA5.PSO.DataType.UByte: var byte2Val = data[eoffset]; ValueTag(sb, cind, ename, byte2Val.ToString()); break; case RageLib.GTA5.PSO.DataType.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 = MetaUtils.SwapBytes(BitConverter.ToInt32(data, eoffset)); var intE = enumInfo.FindEntry(intEVal); StringTag(sb, cind, ename, HashString((MetaName)(intE?.EntryNameHash ?? 0))); break; case 2: //byte enum var byteEVal = data[eoffset]; var byteE = enumInfo.FindEntry(byteEVal); StringTag(sb, cind, ename, HashString((MetaName)(byteE?.EntryNameHash ?? 0))); break; } break; case RageLib.GTA5.PSO.DataType.Flags: uint fCount = ((uint)entry.ReferenceKey >> 16) & 0x0000FFFF; uint fEntry = ((uint)entry.ReferenceKey & 0xFFFF); var fEnt = structInfo.GetEntry((int)fEntry); PsoEnumInfo flagsInfo = null; if ((fEnt != null) && ((MetaName)fEnt.EntryNameHash == MetaName.ARRAYINFO)) { flagsInfo = cont.GetEnumInfo((MetaName)fEnt.ReferenceKey); } if (flagsInfo == null) { flagsInfo = cont.GetEnumInfo((MetaName)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 = MetaUtils.SwapBytes(BitConverter.ToUInt32(data, eoffset)); break; case 1: //short flags flagsVal = MetaUtils.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((MetaName)fentry.EntryNameHash); } } StringTag(sb, cind, ename, fstr); } else { if (fv != 0) { ValueTag(sb, cind, ename, fv.ToString()); } else { SelfClosingTag(sb, cind, ename); } } } break; case RageLib.GTA5.PSO.DataType.Float: var floatVal = MetaUtils.SwapBytes(BitConverter.ToSingle(data, eoffset)); ValueTag(sb, cind, ename, FloatUtil.ToString(floatVal)); break; case RageLib.GTA5.PSO.DataType.Float2: var v2 = MetaUtils.SwapBytes(MetaUtils.ConvertData <Vector2>(data, eoffset)); SelfClosingTag(sb, cind, ename + " x=\"" + FloatUtil.ToString(v2.X) + "\" y=\"" + FloatUtil.ToString(v2.Y) + "\""); break; case RageLib.GTA5.PSO.DataType.Float3: var v3 = MetaUtils.SwapBytes(MetaUtils.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 RageLib.GTA5.PSO.DataType.Float3a: //TODO: check this! var v3a = MetaUtils.SwapBytes(MetaUtils.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 RageLib.GTA5.PSO.DataType.Float4a: //TODO: check this! //...why are there 3 different types of float3? var v3b = MetaUtils.SwapBytes(MetaUtils.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 RageLib.GTA5.PSO.DataType.Float4: var v4 = MetaUtils.SwapBytes(MetaUtils.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 RageLib.GTA5.PSO.DataType.SInt: //TODO: convert hashes? var int5Val = MetaUtils.SwapBytes(BitConverter.ToInt32(data, eoffset)); ValueTag(sb, cind, ename, int5Val.ToString()); break; case RageLib.GTA5.PSO.DataType.UInt: switch (entry.Unk_5h) { default: ErrorXml(sb, cind, ename + ": Unexpected Integer subtype: " + entry.Unk_5h.ToString()); break; case 0: //signed int var int6aVal = MetaUtils.SwapBytes(BitConverter.ToInt32(data, eoffset)); ValueTag(sb, cind, ename, int6aVal.ToString()); break; case 1: //unsigned int var int6bVal = MetaUtils.SwapBytes(BitConverter.ToUInt32(data, eoffset)); ValueTag(sb, cind, ename, "0x" + int6bVal.ToString("X").PadLeft(8, '0')); break; } break; case RageLib.GTA5.PSO.DataType.Long: var long2Val = MetaUtils.SwapBytes(BitConverter.ToUInt64(data, eoffset)); ValueTag(sb, cind, ename, long2Val.ToString()); break; case RageLib.GTA5.PSO.DataType.Map: WriteMapNode(sb, indent, cont, eoffset, entry, structInfo, ename); break; case RageLib.GTA5.PSO.DataType.SShort: var short3Val = (short)MetaUtils.SwapBytes(BitConverter.ToUInt16(data, eoffset)); ValueTag(sb, cind, ename, short3Val.ToString()); break; case RageLib.GTA5.PSO.DataType.UShort: var short4Val = MetaUtils.SwapBytes(BitConverter.ToUInt16(data, eoffset)); ValueTag(sb, cind, ename, short4Val.ToString()); break; case RageLib.GTA5.PSO.DataType.HFloat: //half float? var short1EVal = MetaUtils.SwapBytes(BitConverter.ToUInt16(data, eoffset)); ValueTag(sb, cind, ename, short1EVal.ToString()); break; case RageLib.GTA5.PSO.DataType.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 RageLib.GTA5.PSO.DataType.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 = MetaUtils.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.DataMappingSection.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; } }