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); } }