Esempio n. 1
0
        private void EditRoutineMenuItem_Click(object sender, EventArgs e)
        {
            int idx = listBox_routines.SelectedIndex;

            if (idx >= 0 && idx < listBox_routines.Items.Count)
            {
                var routine = _routines[idx];

                EditValueForm form = new EditValueForm("Edit Dlist",
                                                       "Enter the address and coordinates of the dlist to add.",
                                                       IsInputValid, $"{routine.Address:X8}; {routine.X}; {routine.Y}; {routine.Z}");

                if (form.ShowDialog() == DialogResult.OK)
                {
                    var parts = form.Result.Replace(" ", "").Split(";");

                    routine.X         =
                        routine.Y     =
                            routine.Z = 0;
                    routine.Address   = SegmentedAddress.Parse(parts[0], true);

                    if (parts.Length > 1)
                    {
                        routine.X = int.Parse(parts[1]);
                        routine.Y = int.Parse(parts[2]);
                        routine.Z = int.Parse(parts[3]);
                    }
                    NewRender();
                }
            }
        }
Esempio n. 2
0
        private string IsInputValid(string input)
        {
            string err = "Invalid format, must be \"<address in hex>(; <x>; <y>; <z>)\"";

            var parts = input.Replace(" ", "").Split(";");

            if (parts.Length != 1 && parts.Length != 4)
            {
                return(err);
            }

            string addrStr = parts[0];

            if (addrStr.StartsWith("0x"))
            {
                addrStr = addrStr.Substring(2);
            }

            if (!SegmentedAddress.TryParse(addrStr, true, out SegmentedAddress addr))
            {
                return(err);
            }

            for (int i = 1; i < parts.Length; i++)
            {
                if (!int.TryParse(parts[i], out int res))
                {
                    return(err);
                }
            }

            return(null);
        }
Esempio n. 3
0
        public static void FindAnimations(Z64Object obj, byte[] data, int segmentId)
        {
            // Search for Animation Headers
            // Structure: FF FF 00 00 SS OO OO OO SS OO OO OO II II 00 00
            for (int i = 0; i < data.Length - Z64Object.AnimationHolder.HEADER_SIZE; i += 4)
            {
                var frameCount = ArrayUtil.ReadInt16BE(data, i);
                // check positive nonzero frame count, check struct padding zeroes
                if (frameCount > 0 &&
                    data[i + 2] == 0 && data[i + 3] == 0 && data[i + 14] == 0 && data[i + 15] == 0)
                {
                    if (!obj.IsOffsetFree(i))
                    {
                        continue;
                    }

                    var frameDataSeg    = new SegmentedAddress(ArrayUtil.ReadUint32BE(data, i + 4));
                    var jointIndicesSeg = new SegmentedAddress(ArrayUtil.ReadUint32BE(data, i + 8));
                    // check for segmentId match, check for valid segment offsets
                    if (frameDataSeg.SegmentId == segmentId && jointIndicesSeg.SegmentId == segmentId &&
                        frameDataSeg.SegmentOff < data.Length && jointIndicesSeg.SegmentOff < data.Length)
                    {
                        // Assumes these are all in order and end at the start of the next, which seems to be the case so far
                        int frameDataSize    = (int)(jointIndicesSeg.SegmentOff - frameDataSeg.SegmentOff);
                        int jointIndicesSize = (int)(i - jointIndicesSeg.SegmentOff);

                        // if not a multiple of 2, check for struct padding
                        if ((frameDataSize % 2) != 0)
                        {
                            byte[] possiblePadding = new byte[frameDataSize % 2];
                            Buffer.BlockCopy(data, (int)(frameDataSeg.SegmentOff + frameDataSize - (frameDataSize % 2)),
                                             possiblePadding, 0, frameDataSize % 2);
                            // if assumed struct padding is nonzero, consider invalid
                            if (possiblePadding.Any(b => b != 0))
                            {
                                continue;
                            }
                            frameDataSize -= (frameDataSize % 2);
                        }
                        // if not a multiple of 6, check for struct padding
                        if ((jointIndicesSize % 6) != 0)
                        {
                            byte[] possiblePadding = new byte[jointIndicesSize % 6];
                            Buffer.BlockCopy(data, (int)(jointIndicesSeg.SegmentOff + jointIndicesSize - (jointIndicesSize % 6)),
                                             possiblePadding, 0, jointIndicesSize % 6);
                            // if assumed struct padding is nonzero, consider invalid
                            if (possiblePadding.Any(b => b != 0))
                            {
                                continue;
                            }
                            jointIndicesSize -= (jointIndicesSize % 6);
                        }
                        obj.AddAnimation(off: i);
                        obj.AddFrameData(frameDataSize / 2, off: (int)frameDataSeg.SegmentOff);
                        obj.AddJointIndices(jointIndicesSize / Z64Object.AnimationJointIndicesHolder.ENTRY_SIZE, off: (int)jointIndicesSeg.SegmentOff);
                    }
                }
            }
        }
Esempio n. 4
0
        private static void AddRegion(List <ReservedRegion> regions, int curSegId, uint vaddr, int size, int totalSize)
        {
            var addr = new SegmentedAddress(vaddr);

            if (addr.Segmented && addr.SegmentId == curSegId && ((int)addr.SegmentOff + size) <= totalSize)
            {
                regions.Add(new ReservedRegion((int)addr.SegmentOff, size));
            }
        }
Esempio n. 5
0
        private static void AddDlist(List <int> dlists, int curSegId, uint vaddr, int totalSize)
        {
            var addr = new SegmentedAddress(vaddr);

            if (addr.Segmented && addr.SegmentId == curSegId && (int)addr.SegmentOff < totalSize)
            {
                dlists.Add((int)addr.SegmentOff);
            }
        }
Esempio n. 6
0
 public override void SetData(byte[] data)
 {
     using (var ms = new MemoryStream(data))
     {
         BinaryStream br = new BinaryStream(ms, ByteConverter.Big);
         LimbsSeg  = new SegmentedAddress(br.ReadUInt32());
         LimbCount = br.Read1Byte();
     }
 }
Esempio n. 7
0
        public byte[] ReadBytes(uint vaddr, int count)
        {
            SegmentedAddress addr = ResolveAddress(vaddr, out string path);

            // read data
            try
            {
                if (!addr.Segmented)
                {
                    return(_game.Memory.ReadBytes(vaddr, count));
                }
                else
                {
                    var seg = Segments[addr.SegmentId];
                    switch (seg.Type)
                    {
                    case SegmentType.Fill:
                    {
                        byte[] buff   = new byte[count];
                        int    rest   = count;
                        int    dstOff = 0;
                        while (rest > 0)
                        {
                            int srcOff   = ((int)addr.SegmentOff + dstOff) % seg.Data.Length;
                            int curCount = seg.Data.Length - srcOff;
                            System.Buffer.BlockCopy(seg.Data, srcOff, buff, count - rest, curCount);
                            rest   -= curCount;
                            dstOff += curCount;
                        }
                        return(buff);
                    }

                    case SegmentType.Buffer:
                        if (addr.SegmentOff + count <= seg.Data.Length)
                        {
                            byte[] buff = new byte[count];
                            System.Buffer.BlockCopy(seg.Data, (int)addr.SegmentOff, buff, 0, count);
                            return(buff);
                        }
                        break;

                    case SegmentType.Vram:
                        return(_game.Memory.ReadBytes(seg.Address + addr.SegmentOff, count));

                    case SegmentType.Empty:
                    default:
                        break;
                    }
                }
            }
            catch
            {
            }
            throw new Exception($"Could not read 0x{count:X} bytes at address {path}");
        }
Esempio n. 8
0
 public override void SetData(byte[] data)
 {
     using (var ms = new MemoryStream(data))
     {
         BinaryStream br = new BinaryStream(ms, ByteConverter.Big);
         FrameCount = br.ReadInt16();
         br.ReadBytes(2); // padding
         FrameData      = new SegmentedAddress(br.ReadUInt32());
         JointIndices   = new SegmentedAddress(br.ReadUInt32());
         StaticIndexMax = br.ReadUInt16();
     }
 }
Esempio n. 9
0
 public override void SetData(byte[] data)
 {
     using (MemoryStream ms = new MemoryStream(data))
     {
         BinaryStream br = new BinaryStream(ms, ByteConverter.Big);
         JointX   = br.ReadInt16();
         JointY   = br.ReadInt16();
         JointZ   = br.ReadInt16();
         Child    = br.Read1Byte();
         Sibling  = br.Read1Byte();
         DListSeg = new SegmentedAddress(br.ReadUInt32());
     }
 }
Esempio n. 10
0
            public override void SetData(byte[] data)
            {
                if ((data.Length % 4) != 0)
                {
                    throw new Z64ObjectException($"Invalid data size (0x{data.Length:X}) should be a multiple of 4");
                }

                LimbSegments = new SegmentedAddress[data.Length / 4];
                using (MemoryStream ms = new MemoryStream(data))
                {
                    BinaryStream br = new BinaryStream(ms, ByteConverter.Big);

                    for (int i = 0; i < LimbSegments.Length; i++)
                    {
                        LimbSegments[i] = new SegmentedAddress(br.ReadUInt32());
                    }
                }
            }
Esempio n. 11
0
        private void AddRoutineMenuItem_Click(object sender, System.EventArgs e)
        {
            EditValueForm form = new EditValueForm("Add Dlist", "Enter the address and coordinates of the dlist to add.", IsInputValid);

            if (form.ShowDialog() == DialogResult.OK)
            {
                var parts = form.Result.Replace(" ", "").Split(";");
                int x = 0, y = 0, z = 0;
                var addr = SegmentedAddress.Parse(parts[0], true);
                if (parts.Length > 1)
                {
                    x = int.Parse(parts[1]);
                    y = int.Parse(parts[2]);
                    z = int.Parse(parts[3]);
                }

                AddDList(addr.VAddr, x, y, z);
            }
        }
Esempio n. 12
0
        private void okBtn_Click(object sender, EventArgs e)
        {
            switch (comboBox1.SelectedItem)
            {
            case SRC_ADDR:
                uint addr = SegmentedAddress.Parse(addressValue.Text).VAddr;
                ResultSegment = Memory.Segment.FromVram($"{addr:X8}", addr);
                break;

            case SRC_IDENT_MTX:
                ResultSegment = Memory.Segment.FromFill("Ident Matrices", new byte[] {
                    0, 1, 0, 0, 0, 0, 0, 0,
                    0, 0, 0, 1, 0, 0, 0, 0,
                    0, 0, 0, 0, 0, 1, 0, 0,
                    0, 0, 0, 0, 0, 0, 0, 1,

                    0, 0, 0, 0, 0, 0, 0, 0,
                    0, 0, 0, 0, 0, 0, 0, 0,
                    0, 0, 0, 0, 0, 0, 0, 0,
                    0, 0, 0, 0, 0, 0, 0, 0,
                });
                break;

            case SRC_NULL:
                ResultSegment = Memory.Segment.FromFill("Null Bytes");
                break;

            case SRC_EMPTY:
                ResultSegment = Memory.Segment.Empty();
                break;

            case SRC_EMPTY_DLIST:
                ResultSegment = Memory.Segment.FromBytes("Empty Dlist", new byte[] { 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
                break;

            default:
                break;
            }

            DialogResult = DialogResult.OK;
            Close();
        }
Esempio n. 13
0
        public SegmentedAddress ResolveAddress(uint vaddr, out string path)
        {
            SegmentedAddress addr = new SegmentedAddress(vaddr);

            path = $"{vaddr:X8}";

            // resolve address
            int resolveCount = 0;

            while (addr.Segmented && Segments[addr.SegmentId].Type == SegmentType.Vram)
            {
                if (resolveCount > 16 || new SegmentedAddress(Segments[addr.SegmentId].Address).SegmentId == addr.SegmentId)
                {
                    throw new Exception($"Could not resolve address 0x{vaddr:X}. Path: {path}");
                }

                path += $" -> {Segments[addr.SegmentId].Label}+0x{addr.SegmentOff:X}";
                addr  = new SegmentedAddress(Segments[addr.SegmentId].Address + addr.SegmentOff);
                resolveCount++;
            }
            return(addr);
        }
Esempio n. 14
0
 public byte[] ReadBytes(SegmentedAddress addr, int count) => ReadBytes(addr.VAddr, count);
Esempio n. 15
0
        public static void FindSkeletons(Z64Object obj, byte[] data, int segmentId)
        {
            // Search for Skeleton Headers
            // Structure: SS OO OO OO XX 00 00 00 [XX 00 00 00]
            for (int i = 0; i < data.Length - Z64Object.SkeletonHolder.HEADER_SIZE; i += 4)
            {
                var segment = new SegmentedAddress(ArrayUtil.ReadUint32BE(data, i));
                // check for segmentId match, check for valid segment offset,
                // check for Limbs 0x4 alignment, check for nonzero limb count,
                // check for zeroes in struct padding
                if (segment.SegmentId == segmentId && segment.SegmentOff < data.Length &&
                    (segment.SegmentOff % 4) == 0 && data[i + 4] != 0 &&
                    data[i + 5] == 0 && data[i + 6] == 0 && data[i + 7] == 0)
                {
                    if (!obj.IsOffsetFree(i))
                    {
                        continue;
                    }

                    int    nLimbs    = data[i + 4];
                    byte[] limbsData = new byte[nLimbs * 4];
                    Buffer.BlockCopy(data, (int)segment.SegmentOff, limbsData, 0, nLimbs * 4);
                    // check for limbs array ending at the start of the skeleton header,
                    // check for limbs array's segmented addresses being 0xC apart from one another
                    if (segment.SegmentOff + nLimbs * 4 == i &&
                        ArrayUtil.ReadUint32BE(limbsData, 4) - ArrayUtil.ReadUint32BE(limbsData, 0) == Z64Object.SkeletonLimbHolder.ENTRY_SIZE)
                    {
                        int nNonNullDlists = 0;
                        obj.AddSkeletonLimbs(nLimbs, off: (int)segment.SegmentOff);
                        for (int j = 0; j < nLimbs * 4; j += 4)
                        {
                            SegmentedAddress limbSeg = new SegmentedAddress(ArrayUtil.ReadUint32BE(limbsData, j));
                            if (limbSeg.SegmentId != segmentId)
                            {
                                throw new Z64ObjectAnalyzerException(
                                          $"Limb segment {limbSeg.Segmented} is not the correct segment id, mis-detected SkeletonHeader?");
                            }
                            obj.AddSkeletonLimb(off: (int)limbSeg.SegmentOff);
                            // check if dlist is non-null (dlists may be null in general, this is only for FlexSkeletonHeader detection)
                            if (ArrayUtil.ReadUint32BE(data, (int)(limbSeg.SegmentOff + 0x8)) != 0)
                            {
                                nNonNullDlists++;
                            }
                        }
                        // try to detect flex headers over normal headers
                        // check for the existence of extra bytes beyond standard header size,
                        // check if nothing is already assumed to occupy that space,
                        // check if the number of dlists is equal to the actual number of non-null dlists,
                        // check struct padding
                        if (i < data.Length - Z64Object.FlexSkeletonHolder.HEADER_SIZE && obj.IsOffsetFree(i + Z64Object.SkeletonHolder.HEADER_SIZE) &&
                            data[i + 8] == nNonNullDlists && data[i + 9] == 0 && data[i + 10] == 0 && data[i + 11] == 0)
                        {
                            obj.AddFlexSkeleton(off: i);
                        }
                        else
                        {
                            obj.AddSkeleton(off: i);
                        }
                    }
                }
            }
        }
Esempio n. 16
0
        private void listView_map_SelectedIndexChanged(object sender, EventArgs e)
        {
            var holder = GetCurrentHolder <Z64Object.ObjectHolder>();

            if (holder == null)
            {
                tabControl1.SelectedTab = tabPage_empty;
                return;
            }

            openInDlistViewerMenuItem.Visible          =
                addToDlistViewerMenuItem.Visible       =
                    openSkeletonViewerMenuItem.Visible = false;

            switch (holder.GetEntryType())
            {
            case Z64Object.EntryType.DList:
            {
                openInDlistViewerMenuItem.Visible    =
                    addToDlistViewerMenuItem.Visible = true;

                tabControl1.SelectedTab = tabPage_text;
                UpdateDisassembly();
                break;
            }

            case Z64Object.EntryType.Vertex:
            {
                tabControl1.SelectedTab = tabPage_vtx;
                var vtx = (Z64Object.VertexHolder)holder;

                listView_vtx.BeginUpdate();
                listView_vtx.Items.Clear();
                uint addr = new SegmentedAddress(_segment, _obj.OffsetOf(holder)).VAddr;
                for (int i = 0; i < vtx.Vertices.Count; i++)
                {
                    var item = listView_vtx.Items.Add($"{addr:X8}");
                    item.SubItems.Add($"{vtx.Vertices[i].X}, {vtx.Vertices[i].Y}, {vtx.Vertices[i].Z}");
                    item.SubItems.Add($"0x{vtx.Vertices[i].Flag:X8}");
                    item.SubItems.Add($"{vtx.Vertices[i].TexX}, {vtx.Vertices[i].TexY}");
                    item.SubItems.Add($"{vtx.Vertices[i].R}, {vtx.Vertices[i].G}, {vtx.Vertices[i].B}, {vtx.Vertices[i].A}");
                    addr += 0x10;
                }
                listView_vtx.EndUpdate();

                break;
            }

            case Z64Object.EntryType.Texture:
            {
                tabControl1.SelectedTab = tabPage_texture;
                var tex = (Z64Object.TextureHolder)holder;

                label_textureInfo.Text = $"{tex.Width}x{tex.Height} {tex.Format}";

                if ((tex.Format != N64.N64TexFormat.CI4 && tex.Format != N64.N64TexFormat.CI8) || tex.Tlut != null)
                {
                    pic_texture.Image = tex.GetBitmap();
                }

                if (tex.Tlut != null)
                {
                    uint tlutAddr = new SegmentedAddress(_segment, _obj.OffsetOf(tex.Tlut)).VAddr;
                    label_textureInfo.Text += $" (TLUT : 0x{tlutAddr:X8} {tex.Tlut.Width}x{tex.Tlut.Height} {tex.Tlut.Format})";
                }
                break;
            }

            case Z64Object.EntryType.Mtx:
            {
                tabControl1.SelectedTab = tabPage_text;
                var          matrices = (Z64Object.MtxHolder)holder;
                StringWriter sw       = new StringWriter();
                for (int n = 0; n < matrices.Matrices.Count; n++)
                {
                    sw.WriteLine($" ┌                                                ┐ ");
                    for (int i = 0; i < 4; i++)
                    {
                        var values = "";
                        for (int j = 0; j < 4; j++)
                        {
                            values += $"0x{ArrayUtil.ReadUint32BE(matrices.Matrices[n].GetBuffer(), 4*(4 * i + j)):X08}";
                            if (j != 3)
                            {
                                values += $"  ";
                            }
                        }
                        sw.WriteLine($" │ {values} │ ");
                    }
                    sw.WriteLine($" └                                                ┘ ");
                }
                textBox_holderInfo.Text = sw.ToString();
                break;
            }

            case Z64Object.EntryType.SkeletonHeader:
            {
                openSkeletonViewerMenuItem.Visible = true;
                tabControl1.SelectedTab            = tabPage_text;
                var          skel = (Z64Object.SkeletonHolder)holder;
                StringWriter sw   = new StringWriter();
                sw.WriteLine($"Limbs: 0x{skel.LimbsSeg.VAddr:X8}");
                sw.WriteLine($"Limb Count: {skel.LimbCount}");
                textBox_holderInfo.Text = sw.ToString();
                break;
            }

            case Z64Object.EntryType.FlexSkeletonHeader:
            {
                openSkeletonViewerMenuItem.Visible = true;
                tabControl1.SelectedTab            = tabPage_text;
                var skel = (Z64Object.FlexSkeletonHolder)holder;

                StringWriter sw = new StringWriter();
                sw.WriteLine($"Limbs: 0x{skel.LimbsSeg.VAddr:X8}");
                sw.WriteLine($"Limb Count: {skel.LimbCount}");
                sw.WriteLine($"DList Count: {skel.DListCount}");

                textBox_holderInfo.Text = sw.ToString();
                break;
            }

            case Z64Object.EntryType.SkeletonLimbs:
            {
                tabControl1.SelectedTab = tabPage_text;
                var limbs = (Z64Object.SkeletonLimbsHolder)holder;

                StringWriter sw = new StringWriter();
                sw.WriteLine($"Limbs:");
                foreach (var limb in limbs.LimbSegments)
                {
                    sw.WriteLine($"0x{limb.VAddr:X8}");
                }

                textBox_holderInfo.Text = sw.ToString();
                break;
            }

            case Z64Object.EntryType.SkeletonLimb:
            {
                tabControl1.SelectedTab = tabPage_text;
                var limb = (Z64Object.SkeletonLimbHolder)holder;

                StringWriter sw = new StringWriter();
                sw.WriteLine($"Position: {{ {limb.JointX}, {limb.JointY}, {limb.JointZ} }}");
                sw.WriteLine($"Child: 0x{limb.Child:X2}");
                sw.WriteLine($"Sibling: 0x{limb.Sibling:X2}");
                sw.WriteLine($"DList : 0x{limb.DListSeg.VAddr:X8}");

                textBox_holderInfo.Text = sw.ToString();
                break;
            }

            case Z64Object.EntryType.AnimationHeader:
            {
                tabControl1.SelectedTab = tabPage_text;
                var anim = (Z64Object.AnimationHolder)holder;

                StringWriter sw = new StringWriter();
                sw.WriteLine($"Frame Count: {anim.FrameCount}");
                sw.WriteLine($"Frame Data: 0x{anim.FrameData.VAddr:X8}");
                sw.WriteLine($"Joint Indices: 0x{anim.JointIndices.VAddr:X8}");
                sw.WriteLine($"Static Index Max: 0x{anim.StaticIndexMax}");

                textBox_holderInfo.Text = sw.ToString();
                break;
            }

            case Z64Object.EntryType.JointIndices:
            {
                tabControl1.SelectedTab = tabPage_text;
                var joints = (Z64Object.AnimationJointIndicesHolder)holder;

                StringWriter sw = new StringWriter();
                sw.WriteLine($"Joints:");
                foreach (var joint in joints.JointIndices)
                {
                    sw.WriteLine($"{{ frameData[{joint.X}], frameData[{joint.Y}], frameData[{joint.Z}] }}");
                }

                textBox_holderInfo.Text = sw.ToString();
                break;
            }

            case Z64Object.EntryType.FrameData:
            case Z64Object.EntryType.Unknown:
            {
                tabControl1.SelectedTab = tabPage_unknow;

                var provider = new DynamicByteProvider(holder.GetData());;
                hexBox1.ByteProvider   = provider;
                hexBox1.LineInfoOffset = new SegmentedAddress(_segment, _obj.OffsetOf(holder)).VAddr;
                break;
            }

            default: tabControl1.SelectedTab = tabPage_empty; break;
            }
            listView_map.Focus();
        }
Esempio n. 17
0
 private void addressValue_TextChanged(object sender, EventArgs e)
 {
     okBtn.Enabled = SegmentedAddress.TryParse(addressValue.Text, true, out var _);
 }
Esempio n. 18
0
        public static List <string> AnalyzeDlists(Z64Object obj, byte[] data, int segmentId)
        {
            List <string> errors = new List <string>();

            List <int> dlists = new List <int>();

            for (int i = 0; i < obj.Entries.Count; i++)
            {
                var entry = obj.Entries[i];
                if (entry.GetEntryType() == Z64Object.EntryType.DList)
                {
                    dlists.Add(obj.OffsetOf(entry));
                }
                else
                {
                    obj.Entries[i] = new Z64Object.UnknowHolder($"unk_{obj.OffsetOf(entry):X8}", entry.GetData());
                }
            }

            foreach (var dlist in dlists)
            {
                uint     lastTexAddr              = 0xFFFFFFFF;
                G_IM_FMT lastFmt                  = (G_IM_FMT)(-1);
                G_IM_SIZ lastSiz                  = (G_IM_SIZ)(-1);
                Z64Object.TextureHolder lastTlut  = null;
                Z64Object.TextureHolder lastCiTex = null;

                bool exit = false;
                for (int i = dlist; i < data.Length && !exit; i += 8)
                {
                    CmdID op = (CmdID)data[i];
                    switch (op)
                    {
                    case CmdID.G_QUAD:
                    case CmdID.G_TRI2:
                    case CmdID.G_TRI1:
                    case CmdID.G_TEXRECTFLIP:
                    case CmdID.G_TEXRECT:
                    {
                        if (lastCiTex != null && lastTlut != null)
                        {
                            lastCiTex.Tlut = lastTlut;
                            lastCiTex      = null;
                        }
                        break;
                    }

                    case CmdID.G_ENDDL:
                    {
                        exit = true;
                        break;
                    }

                    case CmdID.G_MTX:
                    {
                        var gmtx = CmdInfo.DecodeCommand <GMtx>(data, i);
                        var addr = new SegmentedAddress(gmtx.mtxaddr);
                        if (addr.Segmented && addr.SegmentId == segmentId)
                        {
                            obj.AddMtx(1, off: (int)addr.SegmentOff);
                        }
                        break;
                    }

                    case CmdID.G_VTX:
                    {
                        var gvtx = CmdInfo.DecodeCommand <GVtx>(data, i);

                        var addr = new SegmentedAddress(gvtx.vaddr);
                        if (addr.Segmented && addr.SegmentId == segmentId)
                        {
                            try
                            {
                                obj.AddVertices(gvtx.numv, off: (int)addr.SegmentOff);
                            }
                            catch (Exception ex)
                            {
                                errors.Add($"Error in Dlist 0x{new SegmentedAddress(segmentId, dlist).VAddr:X8} : {ex.Message}");
                            }
                        }
                        break;
                    }

                    case CmdID.G_SETTIMG:
                    {
                        var settimg = CmdInfo.DecodeCommand <GSetTImg>(data, i);
                        lastTexAddr = settimg.imgaddr;
                        break;
                    }

                    case CmdID.G_SETTILE:
                    {
                        var settile = CmdInfo.DecodeCommand <GSetTile>(data, i);
                        if (settile.tile != G_TX_TILE.G_TX_LOADTILE)
                        {
                            lastFmt = settile.fmt;
                            lastSiz = settile.siz;
                        }
                        break;
                    }

                    case CmdID.G_SETTILESIZE:
                    {
                        var settilesize = CmdInfo.DecodeCommand <GLoadTile>(data, i);
                        var addr        = new SegmentedAddress(lastTexAddr);

                        if ((int)lastFmt == -1 || (int)lastSiz == -1 || lastTexAddr == 0xFFFFFFFF)
                        {
                            /* can't really thow an exception here since in some object files, there are two gsDPSetTileSize next to each other (see object_en_warp_uzu) */
                            //throw new Z64ObjectAnalyzerException();
                            break;
                        }

                        if (addr.Segmented && addr.SegmentId == segmentId)
                        {
                            try
                            {
                                var tex = obj.AddTexture((int)(settilesize.lrs.Float() + 1), (int)(settilesize.lrt.Float() + 1), N64Texture.ConvertFormat(lastFmt, lastSiz), off: (int)addr.SegmentOff);
                                if (lastFmt == G_IM_FMT.G_IM_FMT_CI)
                                {
                                    lastCiTex = tex;
                                }
                            }
                            catch (Exception ex)
                            {
                                errors.Add($"Error in Dlist 0x{new SegmentedAddress(segmentId, dlist).VAddr:X8} : {ex.Message}");
                            }
                        }

                        lastFmt     = (G_IM_FMT)(-1);
                        lastSiz     = (G_IM_SIZ)(-1);
                        lastTexAddr = 0xFFFFFFFF;

                        break;
                    }

                    case CmdID.G_LOADTLUT:
                    {
                        var loadtlut = CmdInfo.DecodeCommand <GLoadTlut>(data, i);
                        var addr     = new SegmentedAddress(lastTexAddr);

                        if (lastTexAddr == 0xFFFFFFFF)
                        {
                            throw new Z64ObjectAnalyzerException();
                        }

                        int w = GetTlutWidth(loadtlut.count + 1);
                        if (addr.Segmented && addr.SegmentId == segmentId)
                        {
                            try
                            {
                                lastTlut = obj.AddTexture(w, (loadtlut.count + 1) / w, N64Texture.ConvertFormat(G_IM_FMT.G_IM_FMT_RGBA, G_IM_SIZ.G_IM_SIZ_16b), off: (int)addr.SegmentOff);
                            }
                            catch (Exception ex)
                            {
                                errors.Add($"Error in Dlist 0x{new SegmentedAddress(segmentId, dlist).VAddr:X8} : {ex.Message}");
                            }
                        }

                        break;
                    }
                    }
                }
            }

            // These are carried out here as they are dependent on a lot of heuristics.
            // Having lots of the object already mapped out reduces possible mis-identifications.
            FindSkeletons(obj, data, segmentId);
            FindAnimations(obj, data, segmentId);

            obj.GroupUnkEntries();
            obj.FixNames();
            obj.SetData(data);

            return(errors);
        }