Example #1
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 #2
0
        public void BackSpace(NitroFile bca, uint addr, uint len, ref BCAOffsetData offs, bool forAlignment = false)
        {
            bca.AddSpace(addr, (uint)-len);
            uint oldRotOffset  = offs.rotOffset;
            uint oldPosOffset  = offs.posOffset;
            uint oldAnimOffset = offs.animOffset;

            if (addr <= offs.scaleOffset)
            {
                offs.scaleOffset -= len;
            }
            if (addr <= offs.rotOffset)
            {
                offs.rotOffset -= len;
            }
            if (addr <= offs.posOffset)
            {
                offs.posOffset -= len;
            }
            if (addr <= offs.animOffset)
            {
                offs.animOffset -= len;
            }

            bca.Write32(0x08, offs.scaleOffset);
            bca.Write32(0x0c, offs.rotOffset);
            bca.Write32(0x10, offs.posOffset);
            bca.Write32(0x14, offs.animOffset);

            if (forAlignment)
            {
                return;
            }

            uint trTypeIndex;

            if (addr > offs.scaleOffset && addr <= oldRotOffset)
            {
                trTypeIndex = 0;
            }
            else if (addr > offs.rotOffset && addr <= oldPosOffset)
            {
                trTypeIndex = 1;
            }
            else if (addr > offs.posOffset && addr <= oldAnimOffset)
            {
                trTypeIndex = 2;
            }
            else
            {
                return;
            }

            uint dataOffset, unitSize;

            offs.GetDataOffsetAndSize(trTypeIndex, out dataOffset, out unitSize);

            uint startSubAt = (addr - dataOffset) / unitSize;
            uint valToSub   = len / unitSize;

            uint numBones = bca.Read16(0x00);

            for (uint i = 0; i < numBones; ++i)
            {
                for (uint j = 0; j < 3; ++j)
                {
                    uint offset = offs.animOffset + 0x24 * i + 4 * (3 * trTypeIndex + j) + 2;
                    uint temp   = bca.Read16(offset);
                    if (temp >= startSubAt)
                    {
                        bca.Write16(offset, (ushort)(temp - valToSub));
                    }
                }
            }
        }
Example #3
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 #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);
            }
        }