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