Exemple #1
0
        /// <summary>
        ///   Parses a limb hierarchy according to the following spec:
        ///   https://wiki.cloudmodding.com/oot/Animation_Format#Hierarchy
        /// </summary>
        public static IList <Limb>?GetHierarchies(
            IBank Data,
            bool isLink,
            DlManager dlManager,
            StaticDlModel model,
            ComboBox dListSelection)
        {
            uint limbIndexAddress;

            model.Reset();
            int j = 0;

            for (int i = 0, loopTo = Data.Count - 8; i <= loopTo; i += 4)
            {
                limbIndexAddress = IoUtil.ReadUInt32(Data, (uint)i);
                IoUtil.SplitAddress(limbIndexAddress,
                                    out var limbIndexBank,
                                    out var limbIndexOffset);
                uint limbCount = Data[i + 4];
                uint limbAddress;

                // Link has an extra set of values for each limb that define LOD model
                // display lists.
                uint limbSize;
                if (isLink)
                {
                    limbSize = 16U;
                }
                else
                {
                    limbSize = 12U;
                }

                if (RamBanks.IsValidBank((byte)limbIndexBank) & limbCount > 0L)
                {
                    var limbIndexBankBuffer = RamBanks.GetBankByIndex(limbIndexBank);

                    if (limbIndexBankBuffer != null &&
                        limbIndexOffset + 4L * limbCount < limbIndexBankBuffer.Count)
                    {
                        byte firstChild;
                        byte nextSibling;
                        bool isValid          = true;
                        bool somethingVisible = false;
                        var  loopTo1          = (int)(limbCount - 1L);
                        for (j = 0; j <= loopTo1; j++)
                        {
                            limbAddress = IoUtil.ReadUInt32(limbIndexBankBuffer,
                                                            (uint)(limbIndexOffset + j * 4));

                            IoUtil.SplitAddress(limbAddress,
                                                out var limbBank,
                                                out var limbOffset);

                            if (!RamBanks.IsValidBank(limbBank))
                            {
                                isValid = false;
                                goto badLimbIndexOffset;
                            }

                            var limbBankBuffer = RamBanks.GetBankByIndex(limbBank);
                            if (limbBankBuffer == null)
                            {
                                isValid = false;
                                goto badLimbIndexOffset;
                            }

                            if (limbOffset + limbSize >= limbBankBuffer.Count)
                            {
                                isValid = false;
                                goto badLimbIndexOffset;
                            }

                            firstChild  = limbBankBuffer[(int)(limbOffset + 6L)];
                            nextSibling = limbBankBuffer[(int)(limbOffset + 7L)];
                            if (firstChild == j | nextSibling == j)
                            {
                                isValid = false;
                                goto badLimbIndexOffset;
                            }

                            var displayListAddress =
                                IoUtil.ReadUInt32(limbBankBuffer, (uint)(limbOffset + 8L));
                            IoUtil.SplitAddress(displayListAddress,
                                                out var displayListBank,
                                                out var displayListOffset);

                            if (displayListBank != 0L)
                            {
                                somethingVisible = true;
                            }

                            if (displayListBank != 0L &
                                !RamBanks.IsValidBank((byte)displayListBank))
                            {
                                isValid = false;
                                goto badLimbIndexOffset;
                            }
                        }

badLimbIndexOffset:

                        if (isValid)
                        {
                            var tmpHierarchy = new Limb[(int)(limbCount - 1L + 1)];
                            for (int k = 0, loopTo2 = (int)(limbCount - 1L);
                                 k <= loopTo2;
                                 k++)
                            {
                                limbAddress = IoUtil.ReadUInt32(limbIndexBankBuffer,
                                                                (uint)(limbIndexOffset +
                                                                       4 * k));
                                IoUtil.SplitAddress(limbAddress,
                                                    out var limbBank,
                                                    out var limbOffset);
                                var limbBankBuffer =
                                    Asserts.Assert(RamBanks.GetBankByIndex(limbBank));

                                {
                                    var limbData =
                                        FinMarshal.Deserialize <LimbData>(
                                            limbBankBuffer,
                                            (int)limbOffset,
                                            true);

                                    var limb = tmpHierarchy[k] = new Limb(limbData);

                                    var displayListAddress = limbData.displayListAddress;
                                    IoUtil.SplitAddress(displayListAddress,
                                                        out var displayListBank,
                                                        out var displayListOffset);

                                    model.AddLimb(limb.Visible,
                                                  limb.x,
                                                  limb.y,
                                                  limb.z,
                                                  limb.firstChild,
                                                  limb.nextSibling);

                                    if (displayListBank != 0L)
                                    {
                                        var displayListBankBuffer =
                                            RamBanks.GetBankByIndex(displayListBank);
                                        DisplayListReader.ReadInDL(dlManager,
                                                                   displayListAddress,
                                                                   dListSelection);
                                    }

                                    // Far model display list (i.e. LOD model). Only used for Link.
                                    // If Data(tmpLimbOff + 12) = Bank Then
                                    // .DisplayListLow = ReadUInt24(Data, tmpLimbOff + 13)
                                    // ReDim Preserve N64DList(N64DList.Length)
                                    // ReadInDL(Data, N64DList, .DisplayListLow, N64DList.Length - 1)
                                    // Else

                                    // End If
                                    PickerUtil.NextRgb(out var r, out var g, out var b);
                                    limb.r = r;
                                    limb.g = g;
                                    limb.b = b;
                                }
                            }

                            if (isValid & !somethingVisible)
                            {
                                throw new NotSupportedException(
                                          "model format is not rendering a valid model!");
                            }

                            return(tmpHierarchy);
                        }
                    }
                }
            }

            return(null);
        }
        /// <summary>
        ///   Parses a set of animations according to the spec at:
        ///   https://wiki.cloudmodding.com/oot/Animation_Format#Normal_Animations
        /// </summary>
        // TODO: Some jank still slips through, is there a proper list of these
        // addresses somewhere in the file?
        public IList <IAnimation>?GetCommonAnimations(
            IBank bank,
            int limbCount,
            ListBox animationList)
        {
            animationList.Items.Clear();

            uint trackCount = (uint)(limbCount * 3);
            var  animations = new List <IAnimation>();

            // Guesstimating the index by looking for an spot where the header's angle
            // address and track address have the same bank as the param at the top.
            for (var i = 4; i < bank.Count - 12; i += 4)
            {
                var attemptOffset = (uint)(i - 4);

                // Verifies the frame count is positive.
                var frameCount = IoUtil.ReadUInt16(bank, attemptOffset);
                if (frameCount == 0)
                {
                    continue;
                }

                var rotationValuesAddress = IoUtil.ReadUInt32(
                    bank,
                    attemptOffset + 4);
                IoUtil.SplitAddress(rotationValuesAddress,
                                    out var rotationValuesBank,
                                    out var rotationValuesOffset);

                // Verifies the rotation values address has a valid bank.
                if (!RamBanks.IsValidBank(rotationValuesBank))
                {
                    continue;
                }

                // Verifies the rotation indices address has a valid bank.
                var rotationIndicesAddress = IoUtil.ReadUInt32(
                    bank,
                    attemptOffset + 8);
                IoUtil.SplitAddress(rotationIndicesAddress,
                                    out var rotationIndicesBank,
                                    out var rotationIndicesOffset);
                if (!RamBanks.IsValidBank(rotationIndicesBank))
                {
                    continue;
                }

                // Obtains the specified banks.
                var rotationValuesBuffer =
                    Asserts.Assert(RamBanks.GetBankByIndex(rotationValuesBank));
                var rotationIndicesBuffer =
                    Asserts.Assert(RamBanks.GetBankByIndex(rotationIndicesBank));

                // Offsets should be within bounds of the bank.
                var validRotationValuesOffset =
                    rotationValuesOffset < rotationValuesBuffer.Count;
                var validRotationIndicesOffset =
                    rotationIndicesOffset < rotationIndicesBuffer.Count;

                if (!validRotationValuesOffset || !validRotationIndicesOffset)
                {
                    continue;
                }

                // Angle count should be greater than 0.
                var angleCount =
                    (uint)((rotationIndicesOffset - rotationValuesOffset) / 2L);
                var validAngleCount = rotationIndicesOffset > rotationValuesOffset &&
                                      angleCount > 0L;
                if (!validAngleCount)
                {
                    continue;
                }

                // Should have zeroes present in two spots of the animation header.
                var hasZeroes =
                    IoUtil.ReadUInt16(bank, attemptOffset + 2) == 0 &&
                    IoUtil.ReadUInt16(bank, attemptOffset + 14) == 0;
                if (!hasZeroes)
                {
                    continue;
                }

                // All values of "tTrack" should be within the bounds of .Angles.
                var validTTracks = true;
                var limit        = IoUtil.ReadUInt16(bank, attemptOffset + 12);
                for (var i1 = 0; i1 < 3 + trackCount; i1++)
                {
                    var tTrack = IoUtil.ReadUInt16(
                        rotationIndicesBuffer,
                        (uint)(rotationIndicesOffset + 2 * i1));
                    if (tTrack < limit)
                    {
                        if (tTrack >= angleCount)
                        {
                            validTTracks = false;
                            goto badTTracks;
                        }
                    }
                    else if ((uint)(tTrack + frameCount) > angleCount)
                    {
                        validTTracks = false;
                        goto badTTracks;
                    }
                }

badTTracks:
                if (!validTTracks)
                {
                    continue;
                }

                var animation = new NormalAnimation {
                    FrameCount  = frameCount,
                    TrackOffset = rotationIndicesOffset,
                    AngleCount  = angleCount
                };

                animation.Angles = new ushort[animation.AngleCount];
                for (var i1 = 0; i1 < animation.AngleCount; ++i1)
                {
                    animation.Angles[i1] =
                        IoUtil.ReadUInt16(rotationValuesBuffer,
                                          rotationValuesOffset);
                    rotationValuesOffset = (uint)(rotationValuesOffset + 2L);
                }

                // Translation is at the start.
                var xList =
                    ReadFrames_(
                        IoUtil.ReadUInt16(rotationIndicesBuffer,
                                          animation.TrackOffset + 0),
                        limit,
                        animation);
                var yList =
                    ReadFrames_(
                        IoUtil.ReadUInt16(rotationIndicesBuffer,
                                          animation.TrackOffset + 2),
                        limit,
                        animation);
                var zList =
                    ReadFrames_(
                        IoUtil.ReadUInt16(rotationIndicesBuffer,
                                          animation.TrackOffset + 4),
                        limit,
                        animation);

                animation.Positions = new Vec3s[animation.FrameCount];
                for (var pi = 0; pi < animation.FrameCount; ++pi)
                {
                    animation.Positions[pi] = new Vec3s {
                        X = (short)xList[Math.Min(pi, xList.Length - 1)],
                        Y = (short)yList[Math.Min(pi, yList.Length - 1)],
                        Z = (short)zList[Math.Min(pi, zList.Length - 1)],
                    };
                }

                animation.Tracks = new NormalAnimationTrack[trackCount];

                var tTrackOffset = (int)(animation.TrackOffset + 6L);
                for (var i1 = 0; i1 < trackCount; ++i1)
                {
                    var track = animation.Tracks[i1] = new NormalAnimationTrack();

                    var tTrack =
                        IoUtil.ReadUInt16(rotationIndicesBuffer,
                                          (uint)tTrackOffset);
                    if (tTrack < limit)
                    {
                        // Constant (single value)
                        track.Type      = 0;
                        track.Frames    = new ushort[1];
                        track.Frames[0] = animation.Angles[tTrack];
                    }
                    else
                    {
                        // Keyframes
                        track.Type   = 1;
                        track.Frames = new ushort[animation.FrameCount];
                        for (var i2 = 0; i2 < animation.FrameCount; ++i2)
                        {
                            try {
                                track.Frames[i2] = animation.Angles[tTrack + i2];
                            } catch {
                                return(null);
                            }
                        }
                    }

                    tTrackOffset += 2;
                }

                animations.Add(animation);

                animationList.Items.Add("0x" + Conversion.Hex(i));
            }

            return(animations.Count > 0 ? animations : null);
        }