예제 #1
0
파일: BCAWriter.cs 프로젝트: awiebe/SM64DSe
        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);
            }
        }