Example #1
0
 public PsoArray4(PsoFile pso, PsoStructureInfo structureInfo, PsoStructureEntryInfo entryInfo, int numberOfEntries)
 {
     this.pso             = pso;
     this.structureInfo   = structureInfo;
     this.entryInfo       = entryInfo;
     this.numberOfEntries = numberOfEntries;
 }
Example #2
0
            public PsoStructureInfo GetStructureInfo(MetaName name)
            {
                PsoStructureInfo i = null;

                StructDict.TryGetValue(name, out i);
                return(i);
            }
Example #3
0
 public PsoStructure(PsoFile pso, PsoStructureInfo structureInfo, PsoElementIndexInfo entryIndexInfo, PsoStructureEntryInfo entryInfo)
 {
     this.pso            = pso;
     this.structureInfo  = structureInfo;
     this.entryIndexInfo = entryIndexInfo;
     this.entryInfo      = entryInfo;
     this.Values         = new Dictionary <int, IPsoValue>();
 }
Example #4
0
 public void AddStructureInfo(MetaName name)
 {
     if (!StructureInfos.ContainsKey(name))
     {
         PsoStructureInfo si = PsoInfo.GetStructureInfo(name);
         if (si != null)
         {
             StructureInfos[name] = si;
         }
     }
 }
Example #5
0
 public PsoMap(
     PsoFile pso,
     PsoStructureInfo structureInfo,
     PsoStructureEntryInfo keyEntryInfo,
     PsoStructureEntryInfo valueEntryInfo)
 {
     this.pso            = pso;
     this.structureInfo  = structureInfo;
     this.keyEntryInfo   = keyEntryInfo;
     this.valueEntryInfo = valueEntryInfo;
 }
Example #6
0
        public static PsoStructureInfo GetStructureInfo(PsoFile meta, int structureKey)
        {
            PsoStructureInfo info = null;

            for (int i = 0; i < meta.DefinitionSection.Count; i++)
            {
                if (meta.DefinitionSection.EntriesIdx[i].NameHash == structureKey)
                {
                    info = (PsoStructureInfo)meta.DefinitionSection.Entries[i];
                }
            }
            return(info);
        }
Example #7
0
        public static IPsoValue Make(PsoFile pso, PsoStructureInfo structureInfo, PsoStructureEntryInfo entryInfo)
        {
            switch (entryInfo.Type)
            {
            case DataType.Array:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0:
                {
                    var t = structureInfo.Entries[entryInfo.ReferenceKey & 0x0000FFFF];
                    return(new PsoArray0(pso, structureInfo, t));
                }

                case 1:
                {
                    var typeIndex = entryInfo.ReferenceKey & 0x0000FFFF;
                    var num       = (entryInfo.ReferenceKey >> 16) & 0x0000FFFF;
                    var t         = structureInfo.Entries[typeIndex];
                    return(new PsoArray1(pso, structureInfo, t, num));
                }

                case 4:
                {
                    var typeIndex = entryInfo.ReferenceKey & 0x0000FFFF;
                    var num       = (entryInfo.ReferenceKey >> 16) & 0x0000FFFF;
                    var t         = structureInfo.Entries[typeIndex];
                    return(new PsoArray4(pso, structureInfo, t, num));
                }

                default:
                {
                    throw new Exception("Unsupported array type.");
                }
                }
            }

            case DataType.String:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0:
                {
                    var len = (entryInfo.ReferenceKey >> 16) & 0x0000FFFF;
                    return(new PsoString0(len));
                }

                case 1:
                {
                    return(new PsoString1());
                }

                case 2:
                {
                    return(new PsoString2());
                }

                case 3:
                {
                    return(new PsoString3());
                }

                case 7:
                {
                    return(new PsoString7());
                }

                case 8:
                {
                    return(new PsoString8());
                }

                default:
                {
                    throw new Exception("Unsupported string type.");
                }
                }
            }

            case DataType.Enum:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0:
                {
                    var entryValue = new PsoEnumInt();
                    entryValue.TypeInfo = GetEnumInfo(pso, entryInfo.ReferenceKey);
                    return(entryValue);
                }

                case 2:
                {
                    var entryValue = new PsoEnumByte();
                    entryValue.TypeInfo = GetEnumInfo(pso, entryInfo.ReferenceKey);
                    return(entryValue);
                }

                default:
                {
                    throw new Exception("Unsupported enum type.");
                }
                }
            }

            case DataType.Flags:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0:
                {
                    var entryValue = new PsoFlagsInt();
                    var sidx       = entryInfo.ReferenceKey & 0x0000FFFF;

                    if (sidx != 0xfff)
                    {
                        var reftype = structureInfo.Entries[sidx];
                        entryValue.TypeInfo = GetEnumInfo(pso, reftype.ReferenceKey);
                    }


                    return(entryValue);
                }

                case 1:
                {
                    var entryValue = new PsoFlagsShort();
                    var sidx       = entryInfo.ReferenceKey & 0x0000FFFF;

                    var reftype = structureInfo.Entries[sidx];
                    entryValue.TypeInfo = GetEnumInfo(pso, reftype.ReferenceKey);

                    return(entryValue);
                }

                case 2:
                {
                    var entryValue = new PsoFlagsByte();
                    var sidx       = entryInfo.ReferenceKey & 0x0000FFFF;
                    var reftype    = structureInfo.Entries[sidx];
                    entryValue.TypeInfo = GetEnumInfo(pso, reftype.ReferenceKey);
                    return(entryValue);
                }

                default:
                {
                    throw new Exception("Unsupported flags type.");
                }
                }
            }

            case DataType.Integer:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoIntSigned());

                case 1: return(new PsoIntUnsigned());

                default: throw new Exception("Unsupported integer type.");
                }
            }

            case DataType.Structure:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0:
                {
                    var t1         = GetStructureInfo(pso, entryInfo.ReferenceKey);
                    var t2         = GetStructureIndexInfo(pso, entryInfo.ReferenceKey);
                    var entryValue = new PsoStructure(pso, t1, t2, entryInfo);
                    return(entryValue);
                }

                case 3:
                {
                    return(new PsoStructure3(pso, structureInfo, entryInfo));
                }

                default:
                {
                    throw new Exception("Unsupported structure type.");
                }
                }
            }

            case DataType.Map:
            {
                switch (entryInfo.Unk_5h)
                {
                case 1:
                {
                    var idx1     = entryInfo.ReferenceKey & 0x0000FFFF;
                    var idx2     = (entryInfo.ReferenceKey >> 16) & 0x0000FFFF;
                    var reftype1 = structureInfo.Entries[idx2];
                    var reftype2 = structureInfo.Entries[idx1];
                    return(new PsoMap(pso, structureInfo, reftype1, reftype2));
                }

                default: throw new Exception("Unsupported PsoType5 type.");
                }
            }

            case DataType.INT_05h:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoType5());

                default: throw new Exception("Unsupported PsoType5 type.");
                }
            }

            case DataType.Byte:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoByte());

                default: throw new Exception("Unsupported PsoByte type.");
                }
            }

            case DataType.Boolean:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoBoolean());

                default: throw new Exception("Unsupported boolean type.");
                }
            }

            case DataType.Float:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoFloat());

                default: throw new Exception("Unsupported float type.");
                }
            }

            case DataType.Float2:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoFloat2());

                default: throw new Exception("Unsupported float2 type.");
                }
            }

            case DataType.Float3:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoFloat4A());

                default: throw new Exception("Unsupported float3 type.");
                }
            }

            case DataType.Float4:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoFloat4B());

                default: throw new Exception("Unsupported float4 type.");
                }
            }

            case DataType.TYPE_09h:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoType9());

                default: throw new Exception("Unsupported PsoType9 type.");
                }
            }

            case DataType.LONG_20h:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoType32());

                default: throw new Exception("Unsupported PsoType32 type.");
                }
            }

            case DataType.SHORT_1Eh:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoXXHalf());

                default: throw new Exception("Unsupported PsoType30 type.");
                }
            }

            case DataType.SHORT_03h:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoType3());

                default: throw new Exception("Unsupported PsoType3 type.");
                }
            }

            case DataType.SHORT_04h:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoType4());

                default: throw new Exception("Unsupported PsoType4 type.");
                }
            }

            case DataType.LONG_01h:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoXXByte());

                default: throw new Exception("Unsupported PsoType1 type.");
                }
            }

            case DataType.TYPE_14h:
            {
                switch (entryInfo.Unk_5h)
                {
                case 0: return(new PsoFloat3());

                default: throw new Exception("Unsupported PsoType20 type.");
                }
            }

            default:
                throw new Exception("Unsupported type.");
            }
        }
Example #8
0
 public PsoArray0(PsoFile pso, PsoStructureInfo structureInfo, PsoStructureEntryInfo entryInfo)
 {
     this.pso           = pso;
     this.structureInfo = structureInfo;
     this.entryInfo     = entryInfo;
 }
Example #9
0
        private void MetaBuildStructureInfos(PsoDefinitionXml xmlInfo)
        {
            //meta.DefinitionSection.EntriesIdx = new List<PsoElementIndexInfo>();
            //meta.DefinitionSection.Entries = new List<PsoElementInfo>();
            strList = new List <Tuple <int, PsoStructureInfo> >();

            foreach (var xmlStructureInfo in xmlInfo.Structures)
            {
                var idxInfo = new PsoElementIndexInfo();
                idxInfo.Offset   = 0;
                idxInfo.NameHash = xmlStructureInfo.NameHash;

                var structureInfo = new PsoStructureInfo();
                structureInfo.Type            = 1;
                structureInfo.Unk             = (byte)xmlStructureInfo.Unknown;
                structureInfo.StructureLength = xmlStructureInfo.Length;
                structureInfo.Entries         = new List <PsoStructureEntryInfo>();
                foreach (var xmlStructureEntryInfo in xmlStructureInfo.Entries)
                {
                    var xmlArrayTypeStack = new Stack <PsoStructureEntryXml>();
                    var xmlArrayType      = xmlStructureEntryInfo.ArrayType;
                    while (xmlArrayType != null)
                    {
                        xmlArrayTypeStack.Push(xmlArrayType);
                        xmlArrayType = xmlArrayType.ArrayType;
                    }

                    while (xmlArrayTypeStack.Count > 0)
                    {
                        xmlArrayType = xmlArrayTypeStack.Pop();
                        var arrayStructureEntryInfo = new PsoStructureEntryInfo();
                        arrayStructureEntryInfo.EntryNameHash = xmlArrayType.NameHash;
                        arrayStructureEntryInfo.Type          = (DataType)xmlArrayType.Type;
                        arrayStructureEntryInfo.Unk_5h        = (byte)xmlArrayType.Unknown;
                        arrayStructureEntryInfo.DataOffset    = (short)xmlArrayType.Offset;
                        if (arrayStructureEntryInfo.Type == DataType.Array)
                        {
                            arrayStructureEntryInfo.ReferenceKey = (short)(structureInfo.Entries.Count - 1);
                        }
                        else
                        {
                            arrayStructureEntryInfo.ReferenceKey = 0;
                        }
                        arrayStructureEntryInfo.ReferenceKey = xmlArrayType.TypeHash;
                        structureInfo.Entries.Add(arrayStructureEntryInfo);
                    }

                    var structureEntryInfo = new PsoStructureEntryInfo();
                    structureEntryInfo.EntryNameHash = xmlStructureEntryInfo.NameHash;
                    structureEntryInfo.Type          = (DataType)xmlStructureEntryInfo.Type;
                    structureEntryInfo.Unk_5h        = (byte)xmlStructureEntryInfo.Unknown;
                    structureEntryInfo.DataOffset    = (short)xmlStructureEntryInfo.Offset;
                    if (structureEntryInfo.Type == DataType.Array)
                    {
                        structureEntryInfo.ReferenceKey = (short)(structureInfo.Entries.Count - 1);
                    }
                    else
                    {
                        structureEntryInfo.ReferenceKey = 0;
                    }
                    structureEntryInfo.ReferenceKey = xmlStructureEntryInfo.TypeHash;

                    structureInfo.Entries.Add(structureEntryInfo);
                }

                strList.Add(new Tuple <int, PsoStructureInfo>(idxInfo.NameHash, structureInfo));
            }
        }
Example #10
0
        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          = MetaUtils.SwapBytes(BitConverter.ToInt32(data, eoffset));      //same as ref key?
                var x2          = MetaUtils.SwapBytes(BitConverter.ToInt32(data, eoffset + 4));  //0?
                var x3          = MetaUtils.SwapBytes(BitConverter.ToInt32(data, eoffset + 8));  //pointer?
                var x4          = MetaUtils.SwapBytes(BitConverter.ToInt32(data, eoffset + 12)); //
                var x5          = MetaUtils.SwapBytes(BitConverter.ToInt32(data, eoffset + 16)); //count/capacity?
                var x6          = MetaUtils.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((MetaName)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((MetaName)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 != RageLib.GTA5.PSO.DataType.String)
                        {
                            ErrorXml(sb, aind, ename + ": Map Key was not a string!");
                        }
                        else if (iEntry.Type != RageLib.GTA5.PSO.DataType.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    = MetaUtils.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((MetaName)iBlock.NameHash) + "\" key=\"" + kStr + "\"";
                                    var iStruc = cont.GetStructureInfo((MetaName)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;
            }
        }
Example #11
0
        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   = ((uint)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 = MetaUtils.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 = MetaUtils.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 RageLib.GTA5.PSO.DataType.Array:
                var rk0 = (entry.ReferenceKey >> 16) & 0x0000FFFF;
                if (rk0 > 0)
                {
                    //var arrStruc5 = MetaUtils.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 RageLib.GTA5.PSO.DataType.Structure:
                switch (arrEntry.Unk_5h)
                {
                case 0:
                    break;

                case 3:        //structure pointer array
                    var arrStrucPtr = MetaUtils.ConvertData <Array_StructurePointer>(data, eoffset);
                    arrStrucPtr.SwapEnd();
                    aBlockId = (int)arrStrucPtr.PointerDataId;
                    aOffset  = (int)arrStrucPtr.PointerDataOffset;
                    aCount   = arrStrucPtr.Count1;
                    if (aCount > 0)
                    {
                        var ptrArr = PsoUtils.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 ((MetaName)aBlock.NameHash != MetaName.PsoPOINTER)
                    {
                        OpenTag(sb, indent, arrTag);
                        if (atyp == null)
                        {
                            ErrorXml(sb, indent, ename + ": Array type not found: " + HashString((MetaName)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 RageLib.GTA5.PSO.DataType.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 = MetaUtils.ConvertData <Array_uint>(data, eoffset);
                    arrHash.SwapEnd();
                    var hashArr = PsoUtils.GetHashArray(cont.Pso, arrHash);
                    WriteItemArray(sb, hashArr, indent, ename, "Hash", HashString);
                    break;
                }
                break;

            case RageLib.GTA5.PSO.DataType.Float2:
                aCount  = ((uint)entry.ReferenceKey >> 16) & 0x0000FFFF;
                arrTag += " itemType=\"Vector2\"";
                var v2Arr = MetaUtils.ConvertDataArray <Vector2>(data, eoffset, (int)aCount);
                WriteRawArray(sb, v2Arr, indent, ename, "Vector2", FormatVector2Swap, 1);
                break;

            case RageLib.GTA5.PSO.DataType.Float3:
                aCount  = ((uint)entry.ReferenceKey >> 16) & 0x0000FFFF;
                arrTag += " itemType=\"Vector3\"";     //this is actually aligned as vector4, the W values are crazy in places
                var v4Arr = MetaUtils.ConvertDataArray <Vector4>(data, eoffset, (int)aCount);
                WriteRawArray(sb, v4Arr, indent, ename, "Vector3", FormatVector4SwapXYZOnly, 1);
                break;

            case RageLib.GTA5.PSO.DataType.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 RageLib.GTA5.PSO.DataType.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 RageLib.GTA5.PSO.DataType.Float:
                var arrFloat = MetaUtils.ConvertData <Array_float>(data, eoffset);
                arrFloat.SwapEnd();
                var floatArr = PsoUtils.GetFloatArray(cont.Pso, arrFloat);
                WriteRawArray(sb, floatArr, indent, ename, "float");
                break;

            case RageLib.GTA5.PSO.DataType.UShort:
                var arrShort = MetaUtils.ConvertData <Array_Structure>(data, eoffset);
                arrShort.SwapEnd();
                var shortArr = PsoUtils.GetUShortArray(cont.Pso, arrShort);
                WriteRawArray(sb, shortArr, indent, ename, "ushort");
                break;

            case RageLib.GTA5.PSO.DataType.UInt:
                var intArr = MetaUtils.ConvertDataArray <int>(data, eoffset, (int)aCount);
                WriteRawArray(sb, intArr, indent, ename, "int");
                break;

            case RageLib.GTA5.PSO.DataType.SInt:
                var arrUint2 = MetaUtils.ConvertData <Array_uint>(data, eoffset);
                arrUint2.SwapEnd();
                var intArr2 = PsoUtils.GetUintArray(cont.Pso, arrUint2);
                WriteRawArray(sb, intArr2, indent, ename, "int");
                break;

            case RageLib.GTA5.PSO.DataType.Enum:
                var arrEnum = MetaUtils.ConvertData <Array_uint>(data, eoffset);
                arrEnum.SwapEnd();
                var enumArr = PsoUtils.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((MetaName)(eval?.EntryNameHash ?? 0)));
                });
                break;
            }
        }