Ejemplo n.º 1
0
            public void Convert(
                uint Width,
                uint Height,
                uint LineSize,
                IList <byte> SourceImg,
                int SourceTexPos,
                ref byte[] DestImg,
                Color4UByte[] _)
            {
                var DestTexPos = 0;

                DestImg = new byte[(int)(Width * Height * 8L + 1)];
                for (int i = 0, loopTo = (int)(Height - 1L); i <= loopTo; i++)
                {
                    for (int j = 0, loopTo1 = (int)(Width / 2L - 1L);
                         j <= loopTo1;
                         j++)
                    {
                        BitMath.Split(SourceImg[SourceTexPos],
                                      out var upper4,
                                      out var lower4);

                        byte IAAlpha;
                        if (Conversions.ToBoolean(upper4 & 1))
                        {
                            IAAlpha = 255;
                        }
                        else
                        {
                            IAAlpha = 0;
                        }
                        var upperIntensity =
                            (byte)Math.Round(IoUtil.ShiftR(upper4, 1, 3) * FACTOR);
                        DestImg[DestTexPos]     = upperIntensity;
                        DestImg[DestTexPos + 1] = upperIntensity;
                        DestImg[DestTexPos + 2] = upperIntensity;
                        DestImg[DestTexPos + 3] = IAAlpha;

                        var lowerIntensity =
                            (byte)Math.Round(IoUtil.ShiftR(lower4, 1, 3) * FACTOR);
                        if (Conversions.ToBoolean(lower4 & 1))
                        {
                            IAAlpha = 255;
                        }
                        else
                        {
                            IAAlpha = 0;
                        }
                        DestImg[DestTexPos + 4] = lowerIntensity;
                        DestImg[DestTexPos + 5] = lowerIntensity;
                        DestImg[DestTexPos + 6] = lowerIntensity;
                        DestImg[DestTexPos + 7] = IAAlpha;

                        ++SourceTexPos;
                        DestTexPos += 8;
                    }

                    SourceTexPos = (int)(SourceTexPos + (LineSize * 8L - Width / 2L));
                }
            }
Ejemplo n.º 2
0
        private void DWordInterleaveWrap_(
            byte[] src,
            uint srcIdx,
            uint srcMask,
            uint numQWords)
        {
            uint p0;
            int  idx0, idx1;

            while (numQWords-- > 0)
            {
                idx0 = (int)(srcIdx++ & srcMask);
                idx1 = (int)(srcIdx++ & srcMask);

                // TODO: Original logic was meant for u32, so we need to think in terms
                // of ints.

                var offset0 = 4 * idx0;
                var offset1 = 4 * idx1;

                p0 = IoUtil.ReadUInt32(src, (uint)offset0);
                var p1 = IoUtil.ReadUInt32(src, (uint)offset1);

                IoUtil.WriteInt32(src, p1, ref offset0);
                IoUtil.WriteInt32(src, p0, ref offset1);
            }
        }
Ejemplo n.º 3
0
        public int GetIndexByAddress(uint address)
        {
            IoUtil.SplitAddress(address, out var bank, out var offset);

            for (var i = 0; i < this.impl_.Count; ++i)
            {
                var zSegment = this.impl_[i].StartPos;
                if (zSegment.Bank == bank && zSegment.Offset == offset)
                {
                    return(i);
                }
            }

            return(-1);
        }
Ejemplo n.º 4
0
        public void Update(uint low, uint high)
        {
            this.Low = low & 0x00ffffff;
            for (var i = 0; i < 4; ++i)
            {
                this.CMDParams[i] = (byte)IoUtil.ShiftR(low, (3 - i) * 8, 8);
            }

            this.High = high;
            for (var i = 0; i < 4; ++i)
            {
                this.CMDParams[4 + i] = (byte)IoUtil.ShiftR(high, (3 - i) * 8, 8);
            }

            this.Opcode = this.CMDParams[0];
        }
Ejemplo n.º 5
0
        /// <summary>
        ///   Shamelessly copied from GLideN64's source.
        /// </summary>
        private void LoadTile32b_(
            ref TileDescriptor tileDescriptor,
            TimgArgs timgArgs)
        {
            var uls = tileDescriptor.ULS;
            var ult = tileDescriptor.ULT;
            var lrs = tileDescriptor.LRS;
            var lrt = tileDescriptor.LRT;

            var width  = lrs - uls + 1;
            var height = lrt - ult + 1;

            var line  = tileDescriptor.LineSize << 2;
            var tbase = tileDescriptor.TmemOffset << 2;

            IoUtil.SplitAddress(timgArgs.Address, out var bank, out var offset);

            var targetBank = Asserts.Assert(RamBanks.GetBankByIndex(bank));

            var timgWidth = timgArgs.Width;

            for (var j = 0; j < height; ++j)
            {
                var tline  = tbase + line * j;
                var s      = ((j + ult) * timgWidth) + uls;
                var xorval = (j & 1) != 0 ? 3 : 1;

                for (var i = 0; i < width; ++i)
                {
                    var addr = offset + s + i;
                    var c    = IoUtil.ReadUInt32(targetBank, (uint)(4 * addr));

                    var ptr = ((tline + i) ^ xorval) & 0x3ff;

                    var offset1 = 2 * ptr;
                    IoUtil.WriteInt16(targetBank, ref offset1, (ushort)(c >> 16));

                    var offset2 = 2 * (ptr | 0x400);
                    IoUtil.WriteInt16(targetBank, ref offset2, (ushort)(c & 0xffff));
                }
            }
        }
Ejemplo n.º 6
0
            public void Convert(
                uint Width,
                uint Height,
                uint LineSize,
                IList <byte> SourceImg,
                int SourceTexPos,
                ref byte[] DestImg,
                Color4UByte[] _)
            {
                var DestTexPos = 0;

                ushort RGBA5551 = 0;

                DestImg = new byte[(int)(Width * Height * 8L + 1)];
                for (var i = 0; i < Height; ++i)
                {
                    for (int j = 0; j < Width; ++j)
                    {
                        RGBA5551            = IoUtil.ReadUInt16(SourceImg, (uint)SourceTexPos);
                        DestImg[DestTexPos] =
                            (byte)Math.Round(IoUtil.ShiftR(RGBA5551, 11, 5) * FACTOR);
                        DestImg[DestTexPos + 1] =
                            (byte)Math.Round(IoUtil.ShiftR(RGBA5551, 6, 5) * FACTOR);
                        DestImg[DestTexPos + 2] =
                            (byte)Math.Round(IoUtil.ShiftR(RGBA5551, 1, 5) * FACTOR);
                        if (Conversions.ToBoolean(RGBA5551 & 1))
                        {
                            DestImg[DestTexPos + 3] = 255;
                        }
                        else
                        {
                            DestImg[DestTexPos + 3] = 0;
                        }
                        SourceTexPos += 2;
                        DestTexPos   += 4;
                    }

                    SourceTexPos = (int)(SourceTexPos + (LineSize * 4L - Width));
                }
            }
Ejemplo n.º 7
0
 public static void WriteInt32(IList <byte> buffer, uint data, ref int offset)
 => IoUtil.WriteInt(buffer, data, ref offset, 4);
Ejemplo n.º 8
0
 public static void WriteInt16(IList <byte> buffer, ref int offset, ushort data)
 => IoUtil.WriteInt(buffer, data, ref offset, 2);
Ejemplo n.º 9
0
 public static uint ReadUInt32(IList <byte> buffer, uint offset)
 => IoUtil.ReadUInt(buffer, offset, 4);
Ejemplo n.º 10
0
 public static ushort ReadUInt16(IList <byte> buffer, uint offset)
 => (ushort)IoUtil.ReadUInt(buffer, offset, 2);
Ejemplo n.º 11
0
        // TODO: Remove combo box input.
        public static int ReadInDL(
            DlManager dlManager,
            uint address,
            ComboBox dListSelection)
        {
            IoUtil.SplitAddress(address, out var bank, out var offset);
            var data = Asserts.Assert(RamBanks.GetBankByIndex(bank));

            try {
                if (offset < data.Count)
                {
                    // TODO: This jumps into the lowest level DL, but the 0xDE command (DL)
                    // actually allows returning back up and calling more DLs. So this seems
                    // like it will sometimes overlook any DLs that follow.
                    // This should just be deleted and replaced w/ emulating in
                    // F3DEX2_Parser.
                    if (data[(int)offset] == 0xDE)
                    {
                        while (data[(int)offset] == 0xDE)
                        {
                            offset = IoUtil.ReadUInt24(data, (uint)(offset + 5L));
                        }
                    }

                    int index       = dlManager.Count;
                    var displayList = new N64DisplayList();
                    dlManager.Add(displayList);
                    uint EPLoc = offset;
                    dListSelection.Items.Add((index + 1).ToString() +
                                             ". " +
                                             Conversion.Hex(offset));
                    displayList.StartPos        = new ZSegment();
                    displayList.StartPos.Offset = offset;
                    displayList.StartPos.Bank   = data.Segment;
                    displayList.Skip            = false;

                    PickerUtil.NextRgb(out var r, out var g, out var b);
                    displayList.PickCol = new Color3UByte {
                        r = r, g = g, b = b
                    };

                    do
                    {
                        var commands = displayList.Commands;
                        Array.Resize(ref commands, displayList.CommandCount + 1);
                        displayList.Commands = commands;
                        displayList.Commands[displayList.CommandCount] =
                            new DLCommand(data, EPLoc);
                        if (data[(int)EPLoc] == (int)F3DZEX.ENDDL | EPLoc >= data.Count)
                        {
                            EPLoc = (uint)(EPLoc + 8L);
                            break;
                        }

                        EPLoc = (uint)(EPLoc + 8L);
                        displayList.CommandCount += 1;
                    } while (true);
                    return((int)EPLoc);
                }
            } catch (Exception ex) {
                Interaction.MsgBox("Error reading in display list: " + ex.Message,
                                   MsgBoxStyle.Critical,
                                   "Exception");
                return(default);
Ejemplo n.º 12
0
        // TODO: The other method passes in "texels", not "lrs"???
        /// <summary>
        ///   Shamelessly copied from GLideN64's source.
        /// </summary>
        public void LoadBlock(
            ref TileDescriptor tileDescriptor,
            uint uls,
            uint ult,
            uint lrs,
            uint dxt,
            TimgArgs timgArgs)
        {
            tileDescriptor.ULS = (int)uls >> 2;
            tileDescriptor.ULT = (int)ult >> 2;
            // TODO: This feels like a bug?
            tileDescriptor.LRS = (int)lrs >> 2;
            tileDescriptor.LRT = (int)dxt >> 2;

            var tmem        = tileDescriptor.TmemOffset;
            var colorFormat = tileDescriptor.ColorFormat;
            var bitSize     = tileDescriptor.BitSize;

            /*if (gSP.DMAOffsets.tex_offset != 0) {
             * if (gSP.DMAOffsets.tex_shift % (((lrs >> 2) + 1) << 3)) {
             *  gDP.textureImage.address -= gSP.DMAOffsets.tex_shift;
             *  gSP.DMAOffsets.tex_offset = 0;
             *  gSP.DMAOffsets.tex_shift = 0;
             *  gSP.DMAOffsets.tex_count = 0;
             * } else
             ++gSP.DMAOffsets.tex_count;
             * }*/

            var timgAddress = timgArgs.Address;
            var timgBitSize = timgArgs.BitSize;
            var timgWidth   = timgArgs.Width;
            var timgBpl     = timgWidth << (int)timgBitSize >> 1;


            IoUtil.SplitAddress(timgAddress, out var bank, out var offset);
            tileDescriptor.Address   = (int)timgAddress;
            tileDescriptor.ImageBank = (int)bank;
            tileDescriptor.Offset    = (int)offset;

            var texture = this.cache_[tileDescriptor];

            if (texture != null)
            {
                return;
            }

            /*gDPLoadTileInfo & info = gDP.loadInfo[gDP.loadTile->tmem];
             * info.texAddress = gDP.loadTile->imageAddress;
             * info.uls = static_cast<u16>(gDP.loadTile->uls);
             * info.ult = static_cast<u16>(gDP.loadTile->ult);
             * info.lrs = static_cast<u16>(gDP.loadTile->lrs);
             * info.lrt = static_cast<u16>(gDP.loadTile->lrt);
             * info.width = static_cast<u16>(gDP.loadTile->lrs);
             * info.dxt = dxt;
             * info.size = static_cast<u8>(gDP.textureImage.size);
             * info.loadType = LOADTYPE_BLOCK;*/

            // TODO: This doesn't look right?
            uint jankWidth = (lrs - uls + 1) & 0x0FFF;
            uint bytes     = jankWidth << (int)bitSize >> 1;

            if ((bytes & 7) != 0)
            {
                bytes = (bytes & (~7U)) + 8;
            }


            //info.bytes = bytes;
            uint address = (uint)(timgAddress +
                                  ult * timgBpl +
                                  (uls << (int)timgBitSize >> 1));

            IoUtil.SplitAddress(address, out var specBank, out var specOffset);



            /*if (bytes == 0 || (address + bytes) > RDRAMSize) {
             * DebugMsg(DEBUG_NORMAL | DEBUG_ERROR, "// Attempting to load texture block out of range\n");
             * DebugMsg(DEBUG_NORMAL, "gDPLoadBlock( %i, %i, %i, %i, %i );\n", tile, uls, ult, lrs, dxt);
             * return;
             * }*/

            /*
             * gDP.loadTile->frameBufferAddress = 0;
             * CheckForFrameBufferTexture(address, info.width, bytes); // Load data to TMEM even if FB texture is found. See comment to texturedRectDepthBufferCopy
             */
            /*var texLowerBound = tileDescriptor.TmemOffset;
             * var texUpperBound = texLowerBound + (bytes >> 3);
             * for (var i = 0; i < tile; ++i) {
             * if (gDP.tiles[i].tmem >= texLowerBound && gDP.tiles[i].tmem < texUpperBound) {
             *  gDPLoadTileInfo & info = gDP.loadInfo[gDP.tiles[i].tmem];
             *  info.loadType = LOADTYPE_BLOCK;
             * }
             * }*/

            var targetBuffer = RamBanks.GetBankByIndex(specBank);

            if (targetBuffer == null)
            {
                return;
            }

            uint tmemAddr = tmem;

            if (bitSize == BitSize.S_32B)
            {
                //gDPLoadBlock32(gDP.loadTile->uls, gDP.loadTile->lrs, dxt);
            }
            else if (colorFormat == ColorFormat.YUV)
            {
                //memcpy(TMEM, &RDRAM[address], bytes); // HACK!
            }
            else
            {
                for (var i = 0; i < bytes; ++i)
                {
                    this.impl_[i] = targetBuffer[(int)(specOffset + i)];
                }

                // TODO: Figure out what the heck this stuff below does.

                /*this.UnswapCopyWrap_(targetBuffer,
                 *                   specOffset,
                 *                   this.impl_,
                 *                   tmemAddr << 3,
                 *                   0xFFF,
                 *                   bytes);*/
                /*if (dxt != 0) {
                 * uint dxtCounter = 0;
                 * uint qwords = (bytes >> 3);
                 * uint line = 0;
                 * while (true) {
                 *  do {
                 ++tmemAddr;
                 *    --qwords;
                 *    if (qwords == 0)
                 *      goto end_dxt_test;
                 *    dxtCounter += dxt;
                 *  } while ((dxtCounter & 0x800) == 0);
                 *  do {
                 ++line;
                 *    --qwords;
                 *    if (qwords == 0)
                 *      goto end_dxt_test;
                 *    dxtCounter += dxt;
                 *  } while ((dxtCounter & 0x800) != 0);
                 *  this.DWordInterleaveWrap_(this.impl_, tmemAddr << 1, 0x3FF, line);
                 *  tmemAddr += line;
                 *  line = 0;
                 * }
                 * end_dxt_test:
                 *
                 * this.DWordInterleaveWrap_(this.impl_, tmemAddr << 1, 0x3FF, line);
                 * }*/
            }
        }
Ejemplo n.º 13
0
        /// <summary>
        ///   Shamelessly copied from GLideN64's source.
        /// </summary>
        public void LoadTile(
            ref TileDescriptor tileDescriptor,
            ushort uls,
            ushort ult,
            ushort lrs,
            ushort lrt,
            TimgArgs timgArgs)
        {
            // TODO: Implement this method.
            // TODO: To verify behavior, load contents into a new texture and save to
            // file.

            tileDescriptor.ULS = uls;
            tileDescriptor.ULT = ult;
            tileDescriptor.LRS = lrs;
            tileDescriptor.LRT = lrt;

            var fUls = IoUtil.Fixed2Float(uls, 2);
            var fUlt = IoUtil.Fixed2Float(ult, 2);
            var fLrs = IoUtil.Fixed2Float(lrs, 2);
            var fLrt = IoUtil.Fixed2Float(lrt, 2);

            var timgImageAddress = timgArgs.Address;
            var timgBitSize      = timgArgs.BitSize;
            var timgWidth        = timgArgs.Width;
            var timgBpl          = timgWidth << (int)timgBitSize >> 1;

            var tmem  = tileDescriptor.TmemOffset;
            var line  = tileDescriptor.LineSize;
            var maskS = tileDescriptor.MaskS;
            var maskT = tileDescriptor.MaskT;

            byte imageBank;
            uint offset;

            IoUtil.SplitAddress(timgImageAddress, out imageBank, out offset);
            tileDescriptor.ImageBank = imageBank;
            tileDescriptor.Offset    = (int)offset;

            var texture = this.cache_[tileDescriptor];

            if (texture != null)
            {
                return;
            }

            if (lrs < uls || lrt < ult)
            {
                return;
            }

            var width  = (lrs - uls + 1) & 0x03FF;
            var height = (lrt - ult + 1) & 0x03FF;
            var bpl    = line << 3;

            var alignedWidth = width;
            var wmask        = 0;

            switch (timgBitSize)
            {
            case BitSize.S_8B:
                wmask = 7;
                break;

            case BitSize.S_16B:
                wmask = 3;
                break;

            case BitSize.S_32B:
                wmask = 1;
                break;

            default:
                throw new NotSupportedException();
            }

            if ((width & wmask) != 0)
            {
                alignedWidth = (width & (~wmask)) + wmask + 1;
            }
            var bpr = alignedWidth << (int)timgBitSize >> 1;

            // Start doing the loading.
            var infoTexAddress = timgImageAddress;
            var infoWidth      = (ushort)(maskS != 0
                                    ? Math.Min(width, 1U << maskS)
                                    : width);
            var infoHeight = (ushort)(maskT != 0
                                     ? Math.Min(height,
                                                1U << maskT)
                                     : height);
            var infoTexWidth = timgWidth;
            var infoSize     = timgBitSize;
            var infoBytes    = bpl * height;

            if (timgBitSize == BitSize.S_32B)
            {
                // 32 bit texture loaded into lower and upper half of TMEM, thus actual bytes doubled.
                infoBytes *= 2;
            }

            if (line == 0)
            {
                return;
            }

            if (maskS == 0)
            {
                tileDescriptor.LoadWidth =
                    Math.Max(tileDescriptor.LoadWidth, infoWidth);
            }
            if (maskT == 0)
            {
                if (Gdp.CycleMode != (int)RdpCycleMode.G_CYC_2CYCLE &&
                    tmem % line == 0)
                {
                    var theight = (ushort)(infoHeight + tmem / line);
                    tileDescriptor.LoadHeight =
                        Math.Max(tileDescriptor.LoadHeight, theight);
                }
                else
                {
                    tileDescriptor.LoadHeight =
                        Math.Max(tileDescriptor.LoadHeight, infoHeight);
                }
            }

            var address = timgImageAddress +
                          ult * timgBpl +
                          (uls << (int)timgBitSize >> 1);

            /*
             * u32 bpl2 = bpl;
             * if (gDP.loadTile->lrs > timgWidth)
             * bpl2 = (gDP.textureImage.width - gDP.loadTile->uls);
             * var height2 = height;
             *
             * if (gDP.loadTile->lrt > gDP.scissor.lry)
             * height2 = static_cast<u32>(gDP.scissor.lry) - gDP.loadTile->ult;
             *
             * if (CheckForFrameBufferTexture(address, info.width, bpl2 * height2))
             * return;*/

            if (timgBitSize == BitSize.S_32B)
            {
                this.LoadTile32b_(ref tileDescriptor, timgArgs);
            }
            else
            {
                ;

                /*u32 tmemAddr = gDP.loadTile->tmem;
                 * const u32 line = gDP.loadTile->line;
                 * const u32 qwpr = bpr >> 3;
                 * for (u32 y = 0; y < height; ++y) {
                 * if (address + bpl > RDRAMSize)
                 *  UnswapCopyWrap(RDRAM,
                 *                 address,
                 *                 reinterpret_cast<u8*>(TMEM),
                 *                 tmemAddr << 3,
                 *                 0xFFF,
                 *                 RDRAMSize - address);
                 * else
                 *  UnswapCopyWrap(RDRAM,
                 *                 address,
                 *                 reinterpret_cast<u8*>(TMEM),
                 *                 tmemAddr << 3,
                 *                 0xFFF,
                 *                 bpr);
                 * if (y & 1)
                 *  DWordInterleaveWrap(reinterpret_cast<u32*>(TMEM),
                 *                      tmemAddr << 1,
                 *                      0x3FF,
                 *                      qwpr);
                 *
                 * address += gDP.textureImage.bpl;
                 * if (address >= RDRAMSize)
                 *  break;
                 * tmemAddr += line;
                 * }*/
            }
        }
Ejemplo n.º 14
0
        /// <summary>
        ///   Parses a set of animations according to the spec at:
        ///   https://wiki.cloudmodding.com/oot/Animation_Format#C_code
        /// </summary>
        public IList <IAnimation>?GetLinkAnimations(
            IBank HeaderData,
            int LimbCount,
            IBank animationData,
            ListBox animationList)
        {
            animationList.Items.Clear();
            var animations = new List <IAnimation>();

            var trackCount = (uint)(LimbCount * 3);
            var frameSize  = 2 * (3 + trackCount) + 2;

            for (uint i = 0x2310; i <= 0x34F8; i += 4)
            {
                // Verifies the frame count is positive.
                var frameCount = IoUtil.ReadUInt16(HeaderData, i);
                if (frameCount == 0)
                {
                    continue;
                }

                var animationAddress = IoUtil.ReadUInt32(HeaderData, i + 4);
                IoUtil.SplitAddress(animationAddress,
                                    out var animationBank,
                                    out var animationOffset);

                // Should use link_animetion bank.
                var validAnimationBank = animationBank == 7;
                if (!validAnimationBank)
                {
                    continue;
                }

                // Should have zeroes in the expected bytes of the header.
                var hasZeroes = IoUtil.ReadUInt16(HeaderData, i + 2) == 0;
                if (!hasZeroes)
                {
                    continue;
                }

                // Should be within the bounds of the bank.
                var validOffset = animationOffset + frameSize * frameCount <
                                  animationData.Count;
                if (!validOffset)
                {
                    continue;
                }

                // Everything looks good with this animation location!

                // Starts parsing animation from this spot.
                var tracks       = new LinkAnimetionTrack[(int)(trackCount - 1L + 1)];
                var positions    = new Vec3s[frameCount];
                var facialStates = new FacialState[frameCount];

                for (int t = 0, loopTo = (int)(trackCount - 1L);
                     t <= loopTo;
                     t++)
                {
                    tracks[t] = new LinkAnimetionTrack(1, new ushort[frameCount]);
                }

                for (int f = 0, loopTo1 = frameCount - 1; f <= loopTo1; f++)
                {
                    var frameOffset = (uint)(animationOffset + f * frameSize);

                    // TODO: This should be ReadInt16() instead.
                    positions[f] = new Vec3s {
                        X = (short)IoUtil.ReadUInt16(animationData, frameOffset),
                        Y = (short)IoUtil.ReadUInt16(animationData, frameOffset + 2),
                        Z = (short)IoUtil.ReadUInt16(animationData, frameOffset + 4),
                    };
                    for (int t = 0, loopTo2 = (int)(trackCount - 1L);
                         t <= loopTo2;
                         t++)
                    {
                        var trackOffset = (uint)(frameOffset + 2 * (3 + t));
                        tracks[t].Frames[f] = IoUtil.ReadUInt16(animationData, trackOffset);
                    }

                    var facialStateOffset =
                        (int)(frameOffset + 2 * (3 + trackCount));
                    var facialState = animationData[facialStateOffset + 1];
                    var mouthState  = IoUtil.ShiftR(facialState, 4, 4);
                    var eyeState    = IoUtil.ShiftR(facialState, 0, 4);

                    facialStates[f] = new FacialState((EyeState)eyeState,
                                                      (MouthState)mouthState);
                }

                var animation =
                    new LinkAnimetion(frameCount, tracks, positions, facialStates);
                animations.Add(animation);

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

            return(animations.Count > 0 ? animations : null);
        }
Ejemplo n.º 15
0
        /// <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);
        }
Ejemplo n.º 16
0
 public void Update(IList <byte> src, uint offset)
 => this.Update(IoUtil.ReadUInt32(src, offset),
                IoUtil.ReadUInt32(src, offset + 4));