/// <summary> /// Reads the data from a compressed track /// </summary> /// <param name="reader"></param> /// <param name="flags"></param> /// <returns></returns> private object[] ReadCompressed(SSBHParser reader, uint flags) { List <object> output = new List <object>(); uint dataOffset = (uint)reader.BaseStream.Position; SSBHAnimCompressedHeader header = reader.ByteToType <SSBHAnimCompressedHeader>(); if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.Boolean)) { ReadBooleans(reader, output, dataOffset, header); } if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.Texture)) { // TODO: What type is this } if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.Float)) { //TODO: What type is this } if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.PatternIndex)) { //TODO: What type is this } if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.Vector4)) { ReadVector4(reader, output, dataOffset, header); } if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.Transform)) { ReadTransform(reader, output, dataOffset, header); } return(output.ToArray()); }
/// <summary> /// Decompresses values in a track /// </summary> /// <param name="parser"></param> /// <param name="dataOffset"></param> /// <param name="header"></param> /// <param name="valueCount"></param> /// <returns></returns> private List <float[]> DecompressValues(SSBHParser parser, uint dataOffset, SSBHAnimCompressedHeader header, int valueCount) { List <float[]> transforms = new List <float[]>(header.FrameCount); // PreProcess SSBHAnimCompressedItem[] items = parser.ByteToType <SSBHAnimCompressedItem>(valueCount); parser.Seek(dataOffset + header.DefaultDataOffset); float[] defaultValues = GetDefaultValues(parser, valueCount); parser.Seek(dataOffset + header.CompressedDataOffset); for (int frameIndex = 0; frameIndex < header.FrameCount; frameIndex++) { // Copy default values. float[] values = new float[valueCount]; for (int i = 0; i < valueCount; i++) { values[i] = defaultValues[i]; } for (int itemIndex = 0; itemIndex < items.Length; itemIndex++) { var item = items[itemIndex]; // Decompress int valueBitCount = (int)item.Count; if (valueBitCount == 0) { continue; } int value = parser.ReadBits(valueBitCount); int scale = 0; for (int k = 0; k < valueBitCount; k++) { scale |= 0x1 << k; } float frameValue = Lerp(item.Start, item.End, 0, 1, value / (float)scale); if (float.IsNaN(frameValue)) { frameValue = 0; } values[itemIndex] = frameValue; } transforms.Add(values); } return(transforms); }
/// <summary> /// decompresses transform values from a track /// </summary> /// <param name="parser"></param> /// <param name="dataOffset"></param> /// <param name="header"></param> /// <returns></returns> private AnimTrackTransform[] DecompressTransform(SSBHParser parser, uint dataOffset, SSBHAnimCompressedHeader header) { AnimTrackTransform[] transforms = new AnimTrackTransform[header.FrameCount]; // PreProcess SSBHAnimCompressedItem[] items = parser.ByteToType <SSBHAnimCompressedItem>(9); parser.Seek(dataOffset + header.DefaultDataOffset); float XSCA = parser.ReadSingle(); float YSCA = parser.ReadSingle(); float ZSCA = parser.ReadSingle(); float XROT = parser.ReadSingle(); float YROT = parser.ReadSingle(); float ZROT = parser.ReadSingle(); float WROT = parser.ReadSingle(); float XPOS = parser.ReadSingle(); float YPOS = parser.ReadSingle(); float ZPOS = parser.ReadSingle(); float CSCA = parser.ReadSingle(); parser.Seek(dataOffset + header.CompressedDataOffset); for (int frame = 0; frame < header.FrameCount; frame++) { AnimTrackTransform transform = new AnimTrackTransform() { X = XPOS, Y = YPOS, Z = ZPOS, RX = XROT, RY = YROT, RZ = ZROT, RW = WROT, SX = XSCA, SY = YSCA, SZ = ZSCA, CompensateScale = CSCA }; for (int itemIndex = 0; itemIndex < items.Length; itemIndex++) { // First check if this track should be parsed // TODO: Don't hard code these flags. if (!((itemIndex == 0 && (header.Flags & 0x3) == 0x3) || //isotropic scale (itemIndex >= 0 && itemIndex <= 2 && (header.Flags & 0x3) == 0x1) || //normal scale (itemIndex > 2 && itemIndex <= 5 && (header.Flags & 0x4) > 0) || (itemIndex > 5 && itemIndex <= 8 && (header.Flags & 0x8) > 0))) { continue; } var item = items[itemIndex]; // Decompress int valueBitCount = (int)item.Count; if (valueBitCount == 0) { continue; } int value = parser.ReadBits(valueBitCount); int scale = 0; for (int k = 0; k < valueBitCount; k++) { scale |= 0x1 << k; } float frameValue = Lerp(item.Start, item.End, 0, 1, value / (float)scale); if (float.IsNaN(frameValue)) { frameValue = 0; } // the Transform type relies a lot on flags if ((header.Flags & 0x3) == 0x3) { //Scale Compensate if (itemIndex == 0) { transform.CompensateScale = frameValue; } } if ((header.Flags & 0x3) == 0x1) { //Scale normal switch (itemIndex) { case 0: transform.SX = frameValue; break; case 1: transform.SY = frameValue; break; case 2: transform.SZ = frameValue; break; } } //Rotation and Position switch (itemIndex) { case 3: transform.RX = frameValue; break; case 4: transform.RY = frameValue; break; case 5: transform.RZ = frameValue; break; case 6: transform.X = frameValue; break; case 7: transform.Y = frameValue; break; case 8: transform.Z = frameValue; break; } } // Rotations have an extra bit at the end if ((header.Flags & 0x4) > 0) { bool wFlip = parser.ReadBits(1) == 1; // W is calculated transform.RW = (float)Math.Sqrt(Math.Abs(1 - (transform.RX * transform.RX + transform.RY * transform.RY + transform.RZ * transform.RZ))); if (wFlip) { transform.RW = -transform.RW; } } transforms[frame] = transform; } return(transforms); }