예제 #1
0
        /// <summary>Loads a new sprite from the given binary stream.</summary>
        /// <param name="animation">The animation this sprite belongs to.</param>
        /// <param name="reader">The binary stream that contains this sprites data.</param>
        /// <param name="id">The ID of the sprite in the animation.</param>
        public SPASprite(SPA animation, SPAReader reader, int id)
        {
            ID        = id;
            Animation = animation;

            // Read some bit flags - these give information about this frame:
            byte flags = reader.ReadByte();

            // Does it also have an alpha frame?
            // If it does, there are two images in this frame (alpha one second).
            bool hasAlphaFrame = ((flags & 1) == 1);

            // Does it have a map?
            // That says where objects are on this sprite.
            bool hasMap = ((flags & 2) == 2);

            // How many frames this sprite holds:
            FrameCount = reader.ReadUInt16();

            // How big is the image, in bytes:
            int dataSize = reader.ReadInt32();

            // Setup the sprite now:
            Sprite = new Texture2D(0, 0);

            // And load the image data:
            Sprite.LoadImage(reader.ReadBytes(dataSize));
            Width  = Sprite.width;
            Height = Sprite.height;

            // Make sure it filters correctly.
            // This is so we don't see parts of other frames around the edge of the image onscreen:
            Sprite.filterMode = FilterMode.Point;

            // Setup the scale:
            TextureScale       = new Vector2((float)Animation.FrameWidth / (float)Width, (float)Animation.FrameHeight / (float)Height);
            VerticalFrameCount = Height / Animation.FrameHeight;

            if (hasAlphaFrame)
            {
                int alphaImageSize = reader.ReadInt32();

                // Setup the temporary alpha texture:
                Texture2D alphaImage = new Texture2D(0, 0);

                // And load it's data:
                alphaImage.LoadImage(reader.ReadBytes(alphaImageSize));

                // Next, merge the alpha pixels into our main sprite.
                Color[] spritePixels = Sprite.GetPixels();
                Color[] alphaPixels  = alphaImage.GetPixels();

                if (spritePixels.Length != alphaPixels.Length)
                {
                    throw new Exception("Invalid SPA alpha channel image.");
                }

                // Set each alpha value from the grayscale of the alpha image:
                for (int i = spritePixels.Length - 1; i >= 0; i--)
                {
                    Color pixel = spritePixels[i];
                    pixel.a         = alphaPixels[i].grayscale;
                    spritePixels[i] = pixel;
                }

                // Write the pixels back:
                Sprite.SetPixels(spritePixels);
            }

            // Has it got a map?
            if (hasMap)
            {
                // Reset the readers X/Y position:
                reader.ResetCoordinates();

                // Yep! Read map flags:
                byte mapFlags = reader.ReadByte();

                // Acting as a font?
                bool isFont = ((mapFlags & 1) == 1);

                // How many entries:
                int count = (int)reader.ReadCompressed();

                // For each map entry..
                for (int i = 0; i < count; i++)
                {
                    SPAMapEntry entry;

                    if (isFont)
                    {
                        // It's a font entry:
                        entry = new SPACharacter(this, reader);
                    }
                    else
                    {
                        // It's a "normal" mapping:
                        entry = new SPAMapEntry(this, reader);
                    }

                    // Add the mapping to the SPA:
                    animation.AddToMap(entry.ID, entry);
                }

                // If it's acting as a font, we've also got things like kerning info.
                if (isFont)
                {
                    // Got kerning?
                    bool hasKerning = ((mapFlags & 2) == 2);

                    // Got additional charcodes?
                    bool additionalCharcodes = ((mapFlags & 4) == 4);

                    // Font meta?
                    bool fontMeta = ((mapFlags & 8) == 8);

                    if (hasKerning)
                    {
                        // Get the # of kerning pairs:
                        int kernCount = (int)reader.ReadCompressed();

                        // Previous char - stored relative for better compression.
                        int previousChar = 0;

                        // For each one..
                        for (int i = 0; i < kernCount; i++)
                        {
                            // Read the first:
                            int firstChar  = (int)reader.ReadCompressed() + previousChar;
                            int secondChar = (int)reader.ReadCompressed();

                            // Advance amount:
                            int advance = (int)reader.ReadCompressedSigned();

                            // Get the char and add a pair to it:
                            SPACharacter before = animation.GetCharacter(firstChar);
                            SPACharacter after  = animation.GetCharacter(secondChar);

                            if (after != null && before != null)
                            {
                                // Add the pair!
                                after.AddKerningPair(before, advance);
                            }

                            // Update previous:
                            previousChar = firstChar;
                        }
                    }

                    // Got additional charcodes?
                    // -> 1 letter with multiple charcodes that use it.

                    if (additionalCharcodes)
                    {
                        // Get the # of additionals:
                        int addCount = (int)reader.ReadCompressed();

                        // Previous char - stored relative for better compression.
                        int previousChar = 0;

                        // For each one..
                        for (int i = 0; i < addCount; i++)
                        {
                            // Read the character ID:
                            int firstChar     = (int)reader.ReadCompressed() + previousChar;
                            int extraCharcode = (int)reader.ReadCompressed();

                            // Get the char:
                            SPACharacter character = animation.GetCharacter(firstChar);

                            // Add the CC to it:
                            character.AddCharcode(extraCharcode);

                            // Add as another charcode:
                            animation.AddToMap(extraCharcode, character);

                            // Update previous:
                            previousChar = firstChar;
                        }
                    }

                    if (fontMeta)
                    {
                        // Load the font meta:
                        Animation.FontMeta = new SPAFontMeta(reader);
                    }
                }
            }
        }
예제 #2
0
        /// <summary>Creates a new SPA animation with the given name from the given binary data.</summary>
        /// <param name="name">The name of the animation. Used for caching purposes so the binary doesn't
        /// have to be reloaded if the animation is displayed multiple times.</param>
        ///	<param name="binaryData">The raw binary data of the spa file.</param>
        public SPA(string name, byte[] binaryData)
        {
            Instances[name] = this;

            SPAReader br = new SPAReader(new MemoryStream(binaryData));

            if (br.ReadChar() != 'S' || br.ReadChar() != 'P' || br.ReadChar() != 'A')
            {
                throw new Exception("This is not an SPA file.");
            }

            byte version = br.ReadByte();

            if (version != 2 && version != 3)
            {
                throw new Exception("This reader supports SPA versions 2 and 3. The file you have given is version " + version + ".");
            }

            int spriteFrames;

            if (version == 2)
            {
                // FR:
                FrameRate = br.ReadByte();

                // Total frame count:
                FrameCount = br.ReadUInt32();

                // Frame width:
                FrameWidth = br.ReadUInt16();

                // Frame height:
                FrameHeight = br.ReadUInt16();

                // Sprite frame count:
                spriteFrames = br.ReadInt32();
            }
            else
            {
                // FR:
                FrameRate = (int)br.ReadCompressed();

                // Total frame count:
                FrameCount = (uint)br.ReadCompressed();

                // Frame width:
                FrameWidth = br.ReadUInt16();

                // Frame height:
                FrameHeight = br.ReadUInt16();

                // Sprite frame count:
                spriteFrames = (int)br.ReadCompressed();
            }

            Sprites = new SPASprite[spriteFrames];

            // Next, read each of the sprite frames:
            for (int i = 0; i < spriteFrames; i++)
            {
                Sprites[i] = new SPASprite(this, br, i);
            }
        }