Example #1
0
        private byte[] GetFile(NitroFile sdat, uint fileID)
        {
            uint fatOffset = sdat.Read32(0x20);

            return(sdat.ReadBlock(sdat.Read32(fatOffset + 12 + 16 * fileID),
                                  sdat.Read32(fatOffset + 16 + 16 * fileID)));
        }
Example #2
0
            public SDAT_File(string name, NitroFile file, uint fileID)
            {
                m_Name = name;
                uint fatOffset = file.Read32(0x20);

                m_Data        = new INitroROMBlock();
                m_Data.m_Data = file.ReadBlock(file.Read32(fatOffset + 12 + 16 * fileID),
                                               file.Read32(fatOffset + 16 + 16 * fileID));
            }
        public void loadSections()
        {
            if (isCompressed)
            {
                throw new Exception("Can't load sections of compressed arm9");
            }

            sections = new List <Arm9BinSection>();

            uint copyTableBegin = (uint)(f.Read32(getCodeSettingsOffs() + 0x00) - Program.m_ROM.ARM9RAMAddress);
            int  copyTableEnd   = (int)(f.Read32(getCodeSettingsOffs() + 0x04) - Program.m_ROM.ARM9RAMAddress);
            int  dataBegin      = (int)(f.Read32(getCodeSettingsOffs() + 0x08) - Program.m_ROM.ARM9RAMAddress);

            newSection((int)Program.m_ROM.ARM9RAMAddress, dataBegin, 0x0, 0);
            sections[0].real = false;

            while (copyTableBegin < copyTableEnd)
            {
                int start = (int)f.Read32(copyTableBegin);
                copyTableBegin += 4;
                int size = (int)f.Read32(copyTableBegin);
                copyTableBegin += 4;
                int bsssize = (int)f.Read32(copyTableBegin);
                copyTableBegin += 4;

                newSection(start, size, dataBegin, bsssize);
                dataBegin += size;
            }
        }
Example #4
0
        public BTP(NitroFile file, BMD model)
        {
            this.m_File = file;
            this.m_FileName = file.m_Name;
            this.m_Model = model;

            m_NumFrames = m_File.Read16(0x00);

            m_NumTextures = m_File.Read16(0x02);
            m_TextureHeadersOffset = m_File.Read32(0x04);
            m_TextureNames = new List<string>();

            m_NumPalettes = m_File.Read16(0x08);
            m_PaletteHeadersOffset = m_File.Read32(0x0C);
            m_PaletteNames = new List<string>();

            m_FrameChangesOffset = m_File.Read32(0x10);
            m_FrameTextureIDsOffset = m_File.Read32(0x14);
            m_FramePaletteIDsOffset = m_File.Read32(0x18);

            m_NumMaterials = m_File.Read16(0x1C);
            m_MaterialHeadersOffset = m_File.Read32(0x20);
            m_MaterialNames = new List<string>();

            for (int i = 0; i < m_NumTextures; i++)
            {
                m_TextureNames.Add(m_File.ReadString(m_File.Read32(m_TextureHeadersOffset + 0x04 + (uint)(i * 8)), 0));
            }

            for (int i = 0; i < m_NumPalettes; i++)
            {
                m_PaletteNames.Add(m_File.ReadString(m_File.Read32(m_PaletteHeadersOffset + 0x04 + (uint)(i * 8)), 0));
            }

            // Read in the frames for each material
            m_MaterialData = new Dictionary<string, BTPMaterialData>();
            m_Frames = new List<BTPFrameData>();
            for (int i = 0; i < m_NumMaterials; i++)
            {
                m_MaterialNames.Add(m_File.ReadString(m_File.Read32(m_MaterialHeadersOffset + 0x04 + (uint)(i * 12)), 0));

                ushort matFrameChanges = m_File.Read16(m_MaterialHeadersOffset + 0x08 + (uint)(i * 12));
                ushort startOffsetFrameChanges = m_File.Read16(m_MaterialHeadersOffset + 0x0A + (uint)(i * 12));

                int matNumFrames = 0;

                for (int j = 0; j < matFrameChanges; j++)
                {
                    ushort frameNum = m_File.Read16((uint)(m_FrameChangesOffset + (startOffsetFrameChanges * 2) + (j * 2)));
                    // If not the last frame in the sequence, length = (next frame number - current frame number) else,
                    // length = (number of frames in sequence - current frame number)
                    int nextFrameNum = (j != (matFrameChanges - 1)) ?
                        (int)m_File.Read16((uint)(m_FrameChangesOffset + (startOffsetFrameChanges * 2) + ((j + 1) * 2))) : -1;
                    int length = (nextFrameNum != -1) ? (nextFrameNum - frameNum) : (m_NumFrames - frameNum);

                    ushort texID = m_File.Read16((uint)(m_FrameTextureIDsOffset + (startOffsetFrameChanges * 2) + (j * 2)));
                    ushort palID = m_File.Read16((uint)(m_FramePaletteIDsOffset + (startOffsetFrameChanges * 2) + (j * 2)));

                    AddFrame(matNumFrames, m_Frames.Count, texID, palID, length);

                    matNumFrames += length;
                }

                BTPMaterialData matData = new BTPMaterialData(m_MaterialNames[i], matFrameChanges, startOffsetFrameChanges, matNumFrames);

                m_MaterialData.Add(m_MaterialNames[i], matData);
            }
        }
Example #5
0
        //the last 2 record types don't exist in SM64DS.

        public SDAT(NitroFile file)
        {
            m_File     = file;
            m_Filename = file.m_Name;

            uint symbolOffset = file.Read32(0x10);
            uint infoOffset   = file.Read32(0x18);

            INitroROMBlock symbolBlock = new INitroROMBlock(file.ReadBlock(file.Read32(0x10), file.Read32(0x14)));
            INitroROMBlock infoBlock   = new INitroROMBlock(file.ReadBlock(file.Read32(0x18), file.Read32(0x1c)));
            INitroROMBlock fatBlock    = new INitroROMBlock(file.ReadBlock(file.Read32(0x20), file.Read32(0x24)));

            fatBlock.m_Data = fatBlock.ReadBlock(0x0c, fatBlock.Read32(0x08) * 0x10); //strip the header

            m_Sequences = new Sequence       [symbolBlock.Read32(symbolBlock.Read32(0x08))];
            m_SeqArcs   = new SequenceArchive[symbolBlock.Read32(symbolBlock.Read32(0x0c))];
            m_Banks     = new Bank           [symbolBlock.Read32(symbolBlock.Read32(0x10))];
            m_WaveArcs  = new SWAR           [symbolBlock.Read32(symbolBlock.Read32(0x14))];
            m_Players   = new Player         [symbolBlock.Read32(symbolBlock.Read32(0x18))];
            m_Groups    = new Group          [symbolBlock.Read32(symbolBlock.Read32(0x1c))];

            INitroROMBlock symbSeqBlock = new INitroROMBlock(
                symbolBlock.ReadBlock(symbolBlock.Read32(0x08) + 4, (uint)m_Sequences.Length * 4));
            INitroROMBlock symbSeqArcBlock = new INitroROMBlock(
                symbolBlock.ReadBlock(symbolBlock.Read32(0x0c) + 4, (uint)m_SeqArcs.Length * 8));
            INitroROMBlock symbBankBlock = new INitroROMBlock(
                symbolBlock.ReadBlock(symbolBlock.Read32(0x10) + 4, (uint)m_Banks.Length * 4));
            INitroROMBlock symbWaveBlock = new INitroROMBlock(
                symbolBlock.ReadBlock(symbolBlock.Read32(0x14) + 4, (uint)m_WaveArcs.Length * 4));
            INitroROMBlock symbPlayerBlock = new INitroROMBlock(
                symbolBlock.ReadBlock(symbolBlock.Read32(0x18) + 4, (uint)m_Players.Length * 4));
            INitroROMBlock symbGroupBlock = new INitroROMBlock(
                symbolBlock.ReadBlock(symbolBlock.Read32(0x1c) + 4, (uint)m_Groups.Length * 4));

            INitroROMBlock infoSeqBlock = new INitroROMBlock(
                infoBlock.ReadBlock(infoBlock.Read32(0x08) + 4, (uint)m_Sequences.Length * 4));
            INitroROMBlock infoSeqArcBlock = new INitroROMBlock(
                infoBlock.ReadBlock(infoBlock.Read32(0x0c) + 4, (uint)m_SeqArcs.Length * 4));
            INitroROMBlock infoBankBlock = new INitroROMBlock(
                infoBlock.ReadBlock(infoBlock.Read32(0x10) + 4, (uint)m_Banks.Length * 4));
            INitroROMBlock infoWaveBlock = new INitroROMBlock(
                infoBlock.ReadBlock(infoBlock.Read32(0x14) + 4, (uint)m_WaveArcs.Length * 4));
            INitroROMBlock infoPlayerBlock = new INitroROMBlock(
                infoBlock.ReadBlock(infoBlock.Read32(0x18) + 4, (uint)m_Players.Length * 4));
            INitroROMBlock infoGroupBlock = new INitroROMBlock(
                infoBlock.ReadBlock(infoBlock.Read32(0x1c) + 4, (uint)m_Groups.Length * 4));

            INitroROMBlock buffer = new INitroROMBlock();

            for (uint i = 0; i < m_WaveArcs.Length; ++i)
            {
                if (infoWaveBlock.Read32(4 * i) == 0) //An offset of 0 means "Does Not Exist".
                {
                    continue;
                }

                uint fileID  = infoBlock.Read32(infoWaveBlock.Read32(4 * i));
                SWAR waveArc = new SWAR(symbolBlock.ReadString(symbWaveBlock.Read32(4 * i), -1),
                                        new INitroROMBlock(GetFile(file, fileID)));
                m_WaveArcs[i] = waveArc;
            }

            for (uint i = 0; i < m_Banks.Length; ++i)
            {
                if (infoBankBlock.Read32(4 * i) == 0)
                {
                    continue;
                }

                buffer.m_Data = infoBlock.ReadBlock(infoBankBlock.Read32(4 * i), 12);
                Bank bank = new Bank();
                bank.m_File = new SDAT_File(symbolBlock.ReadString(symbBankBlock.Read32(4 * i), -1),
                                            file, buffer.Read32(0));
                bank.m_WaveArcs = new SWAR[4];
                for (uint j = 0; j < 4; ++j)
                {
                    uint waveArcID = buffer.Read16(4 + 2 * j);
                    bank.m_WaveArcs[j] = waveArcID != 0xffff ? m_WaveArcs[waveArcID] : null;
                }
                m_Banks[i] = bank;
            }

            for (uint i = 0; i < m_Players.Length; ++i)
            {
                buffer.m_Data = infoBlock.ReadBlock(infoPlayerBlock.Read32(4 * i), 8);
                Player player = new Player();
                player.m_Filename   = symbolBlock.ReadString(symbPlayerBlock.Read32(4 * i), -1);
                player.m_MaxNumSeqs = buffer.Read8(0);
                player.m_HeapSize   = buffer.Read32(4);
                m_Players[i]        = player;
            }

            for (uint i = 0; i < m_Sequences.Length; ++i)
            {
                if (infoSeqBlock.Read32(4 * i) == 0)
                {
                    continue;
                }

                buffer.m_Data = infoBlock.ReadBlock(infoSeqBlock.Read32(4 * i), 12);
                Sequence sequence = new Sequence();
                sequence.m_File = new SDAT_File(symbolBlock.ReadString(symbSeqBlock.Read32(4 * i), -1),
                                                file, buffer.Read32(0));
                sequence.m_Bank            = m_Banks[buffer.Read16(4)];
                sequence.m_Volume          = buffer.Read8(6);
                sequence.m_ChannelPriority = buffer.Read8(7);
                sequence.m_PlayerPriority  = buffer.Read8(8);
                sequence.m_Player          = m_Players[buffer.Read8(9)];
                m_Sequences[i]             = sequence;
            }

            for (uint i = 0; i < m_SeqArcs.Length; ++i)
            {
                if (infoSeqArcBlock.Read32(4 * i) == 0)
                {
                    continue;
                }

                uint            fileID = infoBlock.Read32(infoSeqArcBlock.Read32(4 * i));
                SequenceArchive seqArc = new SequenceArchive();
                seqArc.m_Filename  = symbolBlock.ReadString(symbSeqArcBlock.Read32(8 * i), -1);
                seqArc.m_Sequences = new SequenceArchive.Sequence[
                    symbolBlock.Read32(symbSeqArcBlock.Read32(8 * i + 4))];

                INitroROMBlock symbSeqArcSeqBlock = new INitroROMBlock(symbolBlock.ReadBlock(
                                                                           symbSeqArcBlock.Read32(8 * i + 4) + 4, (uint)seqArc.m_Sequences.Length * 4));
                INitroROMBlock ssar = new INitroROMBlock(GetFile(file, fileID));

                INitroROMBlock ssarInfo = new INitroROMBlock(
                    ssar.ReadBlock(0x20, (uint)seqArc.m_Sequences.Length * 12));
                seqArc.m_Data = new INitroROMBlock(
                    ssar.ReadBlock(ssar.Read32(0x18), (uint)ssar.m_Data.Length - ssar.Read32(0x18)));
                for (uint j = 0; j < seqArc.m_Sequences.Length; ++j)
                {
                    buffer.m_Data = ssarInfo.ReadBlock(12 * j, 12);
                    SequenceArchive.Sequence sequence = new SequenceArchive.Sequence();
                    sequence.m_Filename        = symbolBlock.ReadString(symbSeqArcSeqBlock.Read32(4 * j), -1);
                    sequence.m_SeqOffset       = buffer.Read32(0);
                    sequence.m_Bank            = m_Banks[buffer.Read16(4)];
                    sequence.m_Volume          = buffer.Read8(6);
                    sequence.m_ChannelPriority = buffer.Read8(7);
                    sequence.m_PlayerPriority  = buffer.Read8(8);
                    sequence.m_Player          = m_Players[buffer.Read8(9)];
                    seqArc.m_Sequences[j]      = sequence;
                }
                m_SeqArcs[i] = seqArc;
            }

            for (uint i = 0; i < m_Groups.Length; ++i)
            {
                if (infoGroupBlock.Read32(4 * i) == 0)
                {
                    continue;
                }

                Group group = new Group();
                group.m_Entries = new Group.Entry[
                    infoBlock.Read32(infoGroupBlock.Read32(4 * i))];
                buffer.m_Data = infoBlock.ReadBlock(infoGroupBlock.Read32(4 * i) + 4,
                                                    (uint)group.m_Entries.Length * 8);
                group.m_Filename = symbolBlock.ReadString(symbGroupBlock.Read32(4 * i), -1);

                for (uint j = 0; j < group.m_Entries.Length; ++j)
                {
                    uint        index = buffer.Read32(8 * j + 4);
                    Group.Entry entry = new Group.Entry();
                    entry.m_LoadFlag = buffer.Read8(8 * j + 1);
                    switch (buffer.Read8(8 * j))
                    {
                    case 0:
                        entry.m_Record = m_Sequences[index]; break;

                    case 3:
                        entry.m_Record = m_SeqArcs[index]; break;

                    case 1:
                        entry.m_Record = m_Banks[index]; break;

                    case 2:
                        entry.m_Record = m_WaveArcs[index]; break;

                    default:
                        throw new Exception("Group " + i + ", Record " + j +
                                            " has an unexpected record type: " + buffer.Read8(8 * j));
                    }
                    group.m_Entries[j] = entry;
                }
                m_Groups[i] = group;
            }
        }
Example #6
0
        public void Optimise(NitroFile bca)
        {
            uint numBones  = bca.Read16(0x00);
            uint numFrames = bca.Read16(0x02);

            BCAOffsetData offs = new BCAOffsetData
            {
                scaleOffset = bca.Read32(0x08),
                rotOffset   = bca.Read32(0x0c),
                posOffset   = bca.Read32(0x10),
                animOffset  = bca.Read32(0x14)
            };

            //Pass 1: The constant value pass
            if (numFrames > 1)
            {
                for (uint i = 0; i < numBones; ++i)
                {
                    for (uint j = 0; j < 9; ++j)
                    {
                        if (bca.Read8(offs.animOffset + 0x24 * i + 4 * j + 1) == 0)
                        {
                            continue;
                        }

                        uint dataOffset, unitSize;
                        offs.GetDataOffsetAndSize(j / 3, out dataOffset, out unitSize);

                        bool interped   = bca.Read8(offs.animOffset + 0x24 * i + 4 * j + 0) != 0;
                        uint firstValID = bca.Read16(offs.animOffset + 0x24 * i + 4 * j + 2);
                        uint numVals    = interped ?
                                          numFrames / 2 + 1 :
                                          numFrames;

                        uint[] vals = new uint[numVals];
                        for (uint k = 0; k < numVals; ++k)
                        {
                            vals[k] = bca.ReadVar(dataOffset + (firstValID + k) * unitSize, unitSize);
                        }

                        if (!Array.Exists(vals, x => x != vals[0])) //They're all the same.
                        {
                            uint addr = dataOffset + (firstValID + numVals) * unitSize;
                            BackSpace(bca, addr, (numVals - 1) * unitSize, ref offs);
                            bca.Write16(offs.animOffset + 0x24 * i + 4 * j + 0, 0x0000);
                            continue;
                        }

                        if (interped)
                        {
                            continue;
                        }

                        //Pass 2: The interpolation pass
                        bool  shouldInterp = true;
                        int[] valInts;
                        if (unitSize == 2)
                        {
                            valInts = Array.ConvertAll(vals, x => x >= 0x8000 ? (int)(x | 0xffff0000) : (int)x);
                        }
                        else
                        {
                            valInts = Array.ConvertAll(vals, x => (int)x);
                        }

                        for (int k = 0; k < numVals - 2; k += 2)
                        {
                            if (Math.Abs(valInts[k] - 2 * valInts[k + 1] + valInts[k + 2]) > 1)
                            {
                                shouldInterp = false;
                            }
                        }

                        if (!shouldInterp)
                        {
                            continue;
                        }

                        for (uint k = 0; k < numVals; k += 2)
                        {
                            bca.WriteVar(dataOffset + (firstValID + k / 2) * unitSize, unitSize,
                                         bca.ReadVar(dataOffset + (firstValID + k) * unitSize, unitSize));
                        }
                        if (numVals % 2 == 0) //can't interpolate on last frame even if that frame is odd
                        {
                            bca.WriteVar(dataOffset + (firstValID + numVals / 2) * unitSize, unitSize,
                                         bca.ReadVar(dataOffset + (firstValID + numVals - 1) * unitSize, unitSize));
                        }

                        bca.Write8(offs.animOffset + 0x24 * i + 4 * j + 0, 1);
                        BackSpace(bca, dataOffset + (firstValID + numVals) * unitSize,
                                  (numVals - 1) / 2 * unitSize, ref offs);
                    }
                }
            }

            //Pass 3: The share equal data pass.
            for (uint trID = 0; trID < 3; ++trID)
            {
                uint dataOffset, unitSize;
                offs.GetDataOffsetAndSize(trID, out dataOffset, out unitSize);

                BCAEntryToSort[] bcaEntries = new BCAEntryToSort[3 * numBones];
                for (uint i = 0; i < numBones; ++i)
                {
                    for (uint j = 0; j < 3; ++j)
                    {
                        bool constant   = bca.Read8(offs.animOffset + 0x24 * i + 4 * (3 * trID + j) + 1) == 0;
                        bool interped   = bca.Read8(offs.animOffset + 0x24 * i + 4 * (3 * trID + j) + 0) != 0;
                        uint firstValID = bca.Read16(offs.animOffset + 0x24 * i + 4 * (3 * trID + j) + 2);
                        uint numVals    = constant ? 1 : interped ?
                                          numFrames / 2 + 1 :
                                          numFrames;

                        bcaEntries[3 * i + j].boneID = i;
                        bcaEntries[3 * i + j].axisID = j;
                        bcaEntries[3 * i + j].data   = new uint[numVals];
                        for (uint k = 0; k < numVals; ++k)
                        {
                            bcaEntries[3 * i + j].data[k] =
                                bca.ReadVar(dataOffset + (firstValID + k) * unitSize, unitSize);
                        }
                    }
                }

                BCAEntryToSort[][] sortedEnts = bcaEntries.GroupBy(x => x.data, new UintArrEqualityComparer()).
                                                Select(g => g.ToArray()).
                                                ToArray();

                foreach (BCAEntryToSort[] entArr in sortedEnts)
                {
                    uint boneID = entArr[0].boneID;
                    uint axisID = entArr[0].axisID;

                    for (int i = 1; i < entArr.Length; ++i) //Using the 1st for reference; skip it
                    {
                        bool constant = bca.Read8(offs.animOffset + 0x24 * entArr[i].boneID +
                                                  4 * (3 * trID + entArr[i].axisID) + 1) == 0;
                        bool interped = bca.Read8(offs.animOffset + 0x24 * entArr[i].boneID +
                                                  4 * (3 * trID + entArr[i].axisID) + 0) != 0;
                        uint firstValID = bca.Read16(offs.animOffset + 0x24 * entArr[i].boneID +
                                                     4 * (3 * trID + entArr[i].axisID) + 2);
                        uint numVals = constant ? 1 : interped ?
                                       numFrames / 2 + 1 :
                                       numFrames;

                        uint sameValID = bca.Read16(offs.animOffset + 0x24 * boneID +
                                                    4 * (3 * trID + axisID) + 2);
                        if (firstValID != sameValID) //Avoid deleting entries when they are already shared
                        {
                            bca.Write16(offs.animOffset + 0x24 * entArr[i].boneID +
                                        4 * (3 * trID + entArr[i].axisID) + 2, (ushort)sameValID);
                            BackSpace(bca, dataOffset + (firstValID + numVals) * unitSize,
                                      numVals * unitSize, ref offs);
                        }
                    }
                }
            }

            if ((offs.posOffset - offs.rotOffset) % 4 != 0)
            {
                BackSpace(bca, offs.posOffset, 0xfffffffe, ref offs, true);
            }
        }
Example #7
0
        public BTP(NitroFile file, BMD model)
        {
            this.m_File     = file;
            this.m_FileName = file.m_Name;
            this.m_Model    = model;

            m_NumFrames = m_File.Read16(0x00);

            m_NumTextures          = m_File.Read16(0x02);
            m_TextureHeadersOffset = m_File.Read32(0x04);
            m_TextureNames         = new List <string>();

            m_NumPalettes          = m_File.Read16(0x08);
            m_PaletteHeadersOffset = m_File.Read32(0x0C);
            m_PaletteNames         = new List <string>();

            m_FrameChangesOffset    = m_File.Read32(0x10);
            m_FrameTextureIDsOffset = m_File.Read32(0x14);
            m_FramePaletteIDsOffset = m_File.Read32(0x18);

            m_NumMaterials          = m_File.Read16(0x1C);
            m_MaterialHeadersOffset = m_File.Read32(0x20);
            m_MaterialNames         = new List <string>();

            for (int i = 0; i < m_NumTextures; i++)
            {
                m_TextureNames.Add(m_File.ReadString(m_File.Read32(m_TextureHeadersOffset + 0x04 + (uint)(i * 8)), 0));
            }

            for (int i = 0; i < m_NumPalettes; i++)
            {
                m_PaletteNames.Add(m_File.ReadString(m_File.Read32(m_PaletteHeadersOffset + 0x04 + (uint)(i * 8)), 0));
            }

            // Read in the frames for each material
            m_MaterialData = new Dictionary <string, BTPMaterialData>();
            m_Frames       = new List <BTPFrameData>();
            for (int i = 0; i < m_NumMaterials; i++)
            {
                m_MaterialNames.Add(m_File.ReadString(m_File.Read32(m_MaterialHeadersOffset + 0x04 + (uint)(i * 12)), 0));

                ushort matFrameChanges         = m_File.Read16(m_MaterialHeadersOffset + 0x08 + (uint)(i * 12));
                ushort startOffsetFrameChanges = m_File.Read16(m_MaterialHeadersOffset + 0x0A + (uint)(i * 12));

                int matNumFrames = 0;

                for (int j = 0; j < matFrameChanges; j++)
                {
                    ushort frameNum = m_File.Read16((uint)(m_FrameChangesOffset + (startOffsetFrameChanges * 2) + (j * 2)));
                    // If not the last frame in the sequence, length = (next frame number - current frame number) else,
                    // length = (number of frames in sequence - current frame number)
                    int nextFrameNum = (j != (matFrameChanges - 1)) ?
                                       (int)m_File.Read16((uint)(m_FrameChangesOffset + (startOffsetFrameChanges * 2) + ((j + 1) * 2))) : -1;
                    int length = (nextFrameNum != -1) ? (nextFrameNum - frameNum) : (m_NumFrames - frameNum);

                    ushort texID = m_File.Read16((uint)(m_FrameTextureIDsOffset + (startOffsetFrameChanges * 2) + (j * 2)));
                    ushort palID = m_File.Read16((uint)(m_FramePaletteIDsOffset + (startOffsetFrameChanges * 2) + (j * 2)));

                    AddFrame(matNumFrames, m_Frames.Count, texID, palID, length);

                    matNumFrames += length;
                }

                BTPMaterialData matData = new BTPMaterialData(m_MaterialNames[i], matFrameChanges, startOffsetFrameChanges, matNumFrames);

                m_MaterialData.Add(m_MaterialNames[i], matData);
            }
        }