Пример #1
0
        public void ProcessUpdates(SpriteFrame frameToUpdate, Bitmap bmpWithChanges)
        {
            //update the bitmap, if frameToUpdate.PendingChanges is true
            frameToUpdate.ProcessUpdates(bmpWithChanges);
            Sprite spr = frameToUpdate.ParentSprite;

            List<byte[]> SpriteFrameDataArrays = new List<byte[]>();

            //update the SprBitmapOffset since changes will change the size of the
            //FrameRawData when it gets written to an SPR. The game depends on having
            //the exact offsets to the SPR data.
            int offset = 0;
            for (int i = 0; i < spr.Frames.Count; i++)
            {
                SpriteFrame sf = spr.Frames[i];
                offset += sf.FrameRawData.Length;
                //we depend on short-circuit evaluation here.If i isn't less then the Frames.Count - 1,
                //we'll end up with an out-of-bounds exception. We can't just test for PendingChanges because
                //changes in one SpriteFrame will affect offsets in others, not in itself.
                if ((i < spr.Frames.Count - 1) && (spr.Frames[i + 1].SprBitmapOffset != offset))
                {
                    spr.Frames[i + 1].SprBitmapOffset = offset;
                    //this.PendingChanges = true;

                    foreach (DataRow dr in spr.Frames[i + 1].GameSetDataRows)
                    {
                        dr.BeginEdit();
                        dr[9] = offset.ToString();
                        dr.AcceptChanges(); //calls EndEdit() implicitly
                    }

                    sf.PendingChanges = false;
                }

                //Killing two birds with one for loop. See below.
                SpriteFrameDataArrays.Add(sf.FrameRawData);
            }

            //convert the List<byte[]> to a byte[]
            int lastSize = 0;
            byte[] newSprData = new byte[offset]; //offset now equals the total size of all the frames

            foreach (byte[] b in SpriteFrameDataArrays)
            {
                Buffer.BlockCopy(b, 0, newSprData, lastSize, b.Length);
                lastSize += b.Length;
            }

            this._sprData = newSprData;

            //using (MemoryStream newSprData = new MemoryStream(offset))
            //{
            //    newSprData.SetLength(offset);
            //    int count = 0;
            //    int len = 0;
            //    foreach (SpriteFrame sf in spr.Frames)
            //    {
            //        try
            //        {
            //            //newSprData.SetLength(newSprData.Length + sf.FrameRawData.Length);
            //            newSprData.Write(sf.FrameRawData, (int) newSprData.Position, sf.FrameRawData.Length);
            //            count++;
            //            len += sf.FrameRawData.Length;
            //        }
            //        catch(Exception e)
            //        {
            //            Debugger.Break();
            //        }
            //    }
            //    this._sprData = newSprData.GetBuffer();
            //}
            //this.BuildSpr();
            //this._sprData = BuildSpr();
            //OnSpriteObjectChanged(EventArgs.Empty);
        }
Пример #2
0
        public static void SprStreamToSpriteFrame(SpriteFrame sf, Stream stream)
        {
            sf.SprBitmapOffset = (int) stream.Position;
            //Read Header
            byte[] frame_size_bytes = new byte[8];
            stream.Read(frame_size_bytes, 0, 8);
            int sprSize = BitConverter.ToInt32(frame_size_bytes, 0);
            sf.Width = BitConverter.ToInt16(frame_size_bytes, 4);
            sf.Height = BitConverter.ToInt16(frame_size_bytes, 6);
            sf.PixelSize = sf.Height * sf.Width;
            sf.FrameBmpData = new byte[sf.PixelSize];
            sf.FrameRawData = new byte[sf.PixelSize];

            //initialize it to an unused transparent-pixel-marker
            //todo: Verify 0xff is/isn't used in any of the other sprites
            sf.FrameBmpData = Enumerable.Repeat<byte>(0xff, sf.PixelSize).ToArray();
            sf.FrameRawData = Enumerable.Repeat<byte>(0xff, sf.PixelSize).ToArray();

            Buffer.BlockCopy(frame_size_bytes, 0, sf.FrameRawData, 0, 8);

            int pixelsToSkip = 0;
            int bytesRead = 8; //start after the header info
            byte pixel;

            for (int y = 0; y < sf.Height; ++y)
            {
                for (int x = 0; x < sf.Width; ++x)
                {
                    if (pixelsToSkip != 0)  //only if we've previously identified transparent bits
                    {
                        if (pixelsToSkip >= sf.Width - x) //greater than one line
                        {
                            pixelsToSkip -= (sf.Width - x); // skip to next line
                            break;
                        }

                        x += pixelsToSkip;  //skip reading the indicated amount of bytes for transparent pictures
                        pixelsToSkip = 0;
                    }

                    try
                    {
                        pixel = Convert.ToByte(stream.ReadByte());
                        sf.FrameRawData[bytesRead] = pixel;
                        bytesRead++;
                    }
                    catch
                    {
                        //got -1 for EOF
                        byte[] resize = sf.FrameRawData;
                        Array.Resize<byte>(ref resize, bytesRead);
                        sf.FrameRawData = resize;
                        return;
                    }

                    if (pixel < 0xf8)//MIN_TRANSPARENT_CODE) //normal pixel
                    {
                        sf.FrameBmpData[sf.Width * y + x] = pixel;
                    }
                    else if (pixel == 0xf8)//MANY_TRANSPARENT_CODE)
                    {
                        pixel = Convert.ToByte(stream.ReadByte());
                        pixelsToSkip = pixel - 1;
                        sf.FrameRawData[bytesRead] = pixel;
                        bytesRead++;
                    }
                    else //f9,fa,fb,fc,fd,fe,ff
                    {
                        pixelsToSkip = 256 - pixel - 1;	// skip (neg al) pixels
                    }
                }//end inner for
            }//end outer for

            byte[] resizeMe = sf.FrameRawData;
            Array.Resize<byte>(ref resizeMe, bytesRead);
            sf.FrameRawData = resizeMe;
        }
Пример #3
0
        public static void FrameBmpToSpr(SpriteFrame sf, ColorPalette pal)
        {
            byte palColorByte;
            byte transparentByte = 0xf8;
            int transparentByteCount = 0;
            int realOffset = 8; //since our array offset is unaware of the SPR header data
            byte[] indexedData = new byte[sf.PixelSize + 4];

            // todo: will have to recalculate height/width if bitmap size changes
            byte[] width = BitConverter.GetBytes((short) sf.Width);
            byte[] height = BitConverter.GetBytes((short) sf.Height);

            /**************************************************************************
            *  BitConverter is required, rather than Convert.ToByte(), so we can
            *  get the full 16- or 32-bit representations of the values. This is also
            *  why Height and Width are both cast to short, to ensure we get a 16-bit
            *  representation of each value to match the binary's file format of:
            *  ____________________________________________________________
            *  | 4 byte Size | 2 byte Width | 2 byte Height | byte[] data |
            **************************************************************************/
            int seek_pos = 4; //first four bytes are for SprSize, at the end of the function
            Buffer.BlockCopy(width, 0, indexedData, seek_pos, width.Length);
            seek_pos += width.Length;
            Buffer.BlockCopy(height, 0, indexedData, seek_pos, height.Length);
            seek_pos += height.Length;

            List<Color> Palette = new List<Color>();
            foreach (Color c in pal.Entries)
            {
                Palette.Add(c);
            }

            ////BuildBitmap8bppIndexed() may be called to save the
            ////current image before making changes. So we build
            ////a 32-bit BMP with current FrameData so it can be used
            ////below and to build this SPR to return to the caller.
            //if (sf.ImageBmp == null)
            //    FrameSprToBmp(sf);

            for (int y = 0; y < sf.ImageBmp.Height; ++y)
            {
                for (int x = 0; x < sf.ImageBmp.Width; ++x)
                {
                    Color pixel = sf.ImageBmp.GetPixel(x, y);
                    var pixARGB = pixel.ToArgb();
                    palColorByte = Convert.ToByte(Palette.FindIndex(c => c == Color.FromArgb(pixARGB)));

                    if (palColorByte > 0xf8) //0xf9 - 0xff are transparent
                        transparentByteCount++;

                    // Once we hit a non-zero pixel, we need to write out the transparent pixel marker
                    // and the count of transparent pixels. We then write out the current non-zero pixel.
                    // The second expression after || below is to check if we're on the last pixel of the
                    // image. If so, and the final pixels were colored, there won't be a next pixel to be
                    // below 0xf8 so we need to write it out anyway.
                    bool lastByte = (x == (sf.Width - 1) && (y == (sf.Height - 1)));

                    if (palColorByte <= 0xf8 || lastByte)
                    {
                        if (transparentByteCount > 0)
                        {
                            // Write 0xf8[dd] where [dd] is transparent byte count, unless the
                            // number of transparent bytes is 6 or less, then just use the other
                            // codes below. Seems like the devs were pretty ruthless in trying to
                            // save disk space back in 1997.
                            if (transparentByteCount > 7)
                            {
                                indexedData[realOffset] = transparentByte;
                                realOffset++;
                                indexedData[realOffset] = Convert.ToByte(transparentByteCount);
                                realOffset++;
                                transparentByteCount = 0;
                            }
                            else
                            {
                                //less than 8 and 7kaa cuts down on file size by just writing one byte
                                //transparentByteCount = 2: 0xfe
                                //transparentByteCount = 3: 0xfd
                                //transparentByteCount = 4: 0xfc
                                //transparentByteCount = 5: 0xfb
                                //transparentByteCount = 6: 0xfa
                                //transparentByteCount = 7: 0xf9
                                indexedData[realOffset] = Convert.ToByte(0xff - (transparentByteCount - 1));
                                realOffset++;
                                transparentByteCount = 0;
                            }
                        }

                        //there is no other byte to write out
                        if (!lastByte)
                        {
                            indexedData[realOffset] = palColorByte;
                            realOffset++;
                        }
                    }
                }//end inner for
            }//end outer for

            //subtract four because the int32 size in the header is exclusive of those bytes used for the int32 size
            byte[] size = BitConverter.GetBytes(realOffset - 4);
            if (size.Length > 4)
            {
                string error = $"SPR size must be Int32! Size for {sf.ParentSprite.SpriteId}'s SpriteFrame at offset {realOffset} is {size.ToString()}";
                Trace.WriteLine(error);
                throw new Exception(error);
            }
            Buffer.BlockCopy(size, 0, indexedData, 0, size.Length);

            //Since FrameData is set to ((Width * Height) + 4), its length will
            //be based on the real pixels, not the "compressed" length with
            //the transparent pixels. This makes it impossible to calculate the
            //offsets of the next frames in the sprite to build a new game set.
            Array.Resize<byte>(ref indexedData, realOffset);
            sf.FrameRawData = indexedData;
        }
Пример #4
0
        public static Bitmap FrameSprToBmp(SpriteFrame sf, ColorPalette pal)
        {
            int idx;
            Bitmap bmp = new Bitmap(sf.Width, sf.Height);
            FastBitmap fbmp = new FastBitmap(bmp);
            fbmp.LockImage();

            for (int y = 0; y < sf.Height; y++)
            {
                for (int x = 0; x < sf.Width; x++)
                {
                    idx = sf.FrameBmpData[y * sf.Width + x];
                    Color pixel = pal.Entries[idx];
                    fbmp.SetPixel(x, y, pixel);
                }
            }
            fbmp.UnlockImage();
            Color transparentByte = Color.FromArgb(0xff);
            bmp.MakeTransparent(transparentByte);
            return bmp;
        }
Пример #5
0
        public SpriteFrame AddFrame(SpriteFrame sf = null)
        {
            if (sf == null)
            {
                sf = new SpriteFrame();
                this.Frames.Add(sf);
                return sf;
            }

            this.Frames.Add(sf);
            return sf;
        }
Пример #6
0
        /// <summary>
        /// Opens an SPR file and creates a <see cref="SpriteResource"/> object for it
        /// </summary>
        /// <param name="filepath">The absolute path to the SPR file to open</param>
        /// <returns>The newly-created <see cref="Sprite"/></returns>
        /// <remarks>
        /// The original game code for reading SPR files can be found <code>ResourceDb::init_imported()</code> 
        /// in src/ORESDB.cpp around line 72. The <code>resName</code> will be "sprite\\NAME.SPR". (There's 
        /// no need to follow the call into <code>File::file_open()</code> in OFILE.cpp. Though the files are 
        /// well-structured, they are considered FLAT by 7KAA.
        /// </remarks>
        public void LoadSprite(string filepath)
        {
            if (this.ActivePalette == null)
                throw new Exception("Cannot load a Sprite if the ActivePalette is null.");

            //cant use the property here or we'll fire the event before we've finished loading
            Sprite spr = new Sprite(this.ActivePalette);
            //this._activeSprite = new Sprite(this.ActivePalette);
            
            using (FileStream spritestream = File.OpenRead(filepath))
            {
                while (spritestream.Position < spritestream.Length)
                {
                    SpriteFrame sf = new SpriteFrame(spr);
                    SprDataHandlers.SprStreamToSpriteFrame(sf, spritestream);
                    sf.ImageBmp = SprDataHandlers.FrameSprToBmp(sf, this.ActivePalette);
                    spr.Frames.Add(sf);
                }
            }

            spr.Resource.FileName = Path.GetFileName(filepath);
            spr.SpriteId = Path.GetFileNameWithoutExtension(filepath);
            DataView dv = this.ActiveGameSet.GetSpriteDataView(spr.SpriteId);
            spr.SetSpriteDataView(dv);
            this.ActiveSprite = spr;
        }