Don't like ads? PRO users don't see any ads ;-)
Guest

Myth of Soma Sprite.cs

By: a guest on Jul 30th, 2012  |  syntax: C#  |  size: 29.18 KB  |  hits: 25  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /*
  2. #   Soma.Sprite DLL
  3. #       Myth of Soma: Sword of the Lion
  4. #       http://www.swordofthelion.net
  5. #       ----------------------------------------------------
  6. #       File:           Sprite.cs
  7. #   Version:    3.0.0       21/05/2010
  8. #       Date(s):        06/03/2009  Original (C++)
  9. #               24/08/2009  Rewritten (C++)
  10. #               14/04/2010  Rewritten (C#)
  11. #       Author(s):      Matthew Bowen
  12. #
  13. #   Contains the Sprite class which is used to load
  14. #   and manage Myth of Soma game sprites.  Soma sprites
  15. #   are held in SPL, MRF and OBM file-types.
  16. #       ----------------------------------------------------
  17. */
  18.  
  19. using System;
  20. using System.Drawing;                       // Bitmaps
  21. using System.IO;                            // File IO
  22. using System.Security.Permissions;          // File Read/Write permissions
  23. using System.Text;                          // Strings from byte arrays
  24.  
  25. // Disable warning of unused variables.
  26. // Used to supress the warning of unused exception variables.
  27. #pragma warning disable 168
  28.  
  29. namespace Soma
  30. {
  31.     // XNA also defines Color - specify which we use to avoid ambiguity
  32.     using Color = System.Drawing.Color;
  33.  
  34.     /// <summary>
  35.     /// Represents a Myth of Soma game sprite.
  36.     /// </summary>
  37.     public class Sprite
  38.     {
  39.         #region Constants
  40.         private const int MaxPaletteSize = 256;
  41.         private readonly Color DefaultTransparency = Color.FromArgb(0xf8, 0x00, 0xf8);
  42.         #endregion
  43.  
  44.         #region Structs
  45.         /// <summary>
  46.         /// Represents the header of a Myth of Soma SPL file.
  47.         /// </summary>
  48.         private struct SplHeader
  49.         {
  50.             // NOTE: TO ADD: color array of palette data
  51.  
  52.             // Size of all SPL header structs in bytes (as contained in an SPL file)
  53.             public const int StructSize = 148;
  54.  
  55.             private string type;            // SPLN or SPL8
  56.             private string notes;           // Can be used to store a short note on the file
  57.             private Color transparency;     // Colour to use as transparency
  58.             private uint frameQty;          // Number of frames in sprite
  59.  
  60.             // Maximum length for the notes string
  61.             // (this value differs from Soma as I have combined szRemark with szBMPFN
  62.             //  as the latter is unnecessary)
  63.             private const ushort maxNotesLength = 128;
  64.  
  65.             /// <summary>
  66.             /// Creates a new SPL header.
  67.             /// </summary>
  68.             /// <param name="splType">Either "SPLN" (uncompressed) or "SPL8" (compressed).</param>
  69.             /// <param name="splNotes">Optional notes on the file.</param>
  70.             /// <param name="ck">Colour key representing transparency.</param>
  71.             /// <param name="frames">Number of frames in the sprite.</param>
  72.             public SplHeader(string splType, string splNotes, Color ck, uint frames)
  73.             {
  74.                 type = splType;
  75.                 notes = splNotes;
  76.                 transparency = ck;
  77.                 frameQty = frames;
  78.             }
  79.  
  80.             /// <summary>
  81.             /// Gets or sets the SPL's type - use either SPLN or SPL8.
  82.             /// </summary>
  83.             public string Type
  84.             {
  85.                 get { return type; }
  86.                 set
  87.                 {
  88.                     // Only two types of SPL accepted
  89.                     if (value == "SPLN" || value == "SPL8")
  90.                         type = value;
  91.                 }
  92.             }
  93.  
  94.             /// <summary>
  95.             /// Gets a value indicating whether the SPL is compressed (is a SPL8 file).
  96.             /// </summary>
  97.             public bool IsCompressed
  98.             {
  99.                 get { return (type == "SPL8"); }
  100.             }
  101.  
  102.             /// <summary>
  103.             /// Gets or sets the short 128-character note on the file.
  104.             /// </summary>
  105.             public string Notes
  106.             {
  107.                 get { return notes; }
  108.                 set
  109.                 {
  110.                     if (value.Length <= maxNotesLength)
  111.                         notes = value;
  112.                 }
  113.             }
  114.  
  115.             /// <summary>
  116.             /// Gets or sets the colour to be used as the transparency colour.
  117.             /// </summary>
  118.             public Color ColourKey
  119.             {
  120.                 get { return transparency; }
  121.                 set { transparency = value; }
  122.             }
  123.  
  124.             /// <summary>
  125.             /// Gets a value representing the number of frames in this SPL.
  126.             /// </summary>
  127.             public uint Frames
  128.             {
  129.                 get { return frameQty; }
  130.             }
  131.         }
  132.  
  133.         /// <summary>
  134.         /// Represents the header of a Myth of Soma SPL frame.
  135.         /// </summary>
  136.         public struct SplFrameHeader
  137.         {
  138.             // Size of all SPL frame header structs in bytes (as contained in an SPL file)
  139.             public const int StructSize = 28;
  140.  
  141.             // Size of the frame data in the SPL file in bytes
  142.             private int size;
  143.  
  144.             // Area in pixels occupied by the frame
  145.             // Left and Top store the offsets used to draw this frame relative to the location
  146.             // on the screen where this frame is to be drawn (used so that frames of different
  147.             // dimensions animate smoothly with the image appearing to be drawn in the same place)
  148.             private Rectangle area;
  149.  
  150.             /// <summary>
  151.             /// Creates a new SPL frame header.
  152.             /// </summary>
  153.             /// <param name="rect">Rectangle representing the area used by the frame.</param>
  154.             /// <param name="dataSize">Size of the frame data in the SPL file in bytes.</param>
  155.             public SplFrameHeader(Rectangle rect, int dataSize)
  156.             {
  157.                 area = rect;
  158.                 size = dataSize;
  159.             }
  160.  
  161.             /// <summary>
  162.             /// Gets a Rectangle object representing the area occupied by the frame.
  163.             /// </summary>
  164.             public Rectangle Area
  165.             {
  166.                 get
  167.                 {
  168.                     return area;
  169.                 }
  170.             }
  171.  
  172.             /// <summary>
  173.             /// Gets a value representing the number of bytes required to store this frame in the SPL.
  174.             /// </summary>
  175.             public int DataSize
  176.             {
  177.                 get { return size; }
  178.             }
  179.         }
  180.  
  181.         /// <summary>
  182.         /// Represents a single sprite frame.
  183.         /// </summary>
  184.         public struct Frame
  185.         {
  186.             private SplFrameHeader header;
  187.             private byte[] data;                    // Frame's image data read from file
  188.             private IntPtr texture;                 // Pointer to DirectX texture
  189.  
  190.             /// <summary>
  191.             /// Gets this frame's DirectX texture
  192.             /// </summary>
  193.             public SplFrameHeader Header
  194.             {
  195.                 get { return header; }
  196.             }
  197.  
  198.             /// <summary>
  199.             /// Gets or sets the pointer (LPDIRECT3DTEXTURE9) to the DirectX texture for this frame.
  200.             /// </summary>
  201.             public IntPtr Texture
  202.             {
  203.                 get { return texture; }
  204.                 set { texture = value; }
  205.             }
  206.  
  207.             /// <summary>
  208.             /// Gets or sets the frame's image data as read from the file.
  209.             /// </summary>
  210.             public byte[] ImageData
  211.             {
  212.                 get { return data; }
  213.                 set { data = value; }
  214.             }
  215.  
  216.             /// <summary>
  217.             /// Gets a value representing the total number of pixels in the frame (width * height).
  218.             /// </summary>
  219.             public int TotalPixels
  220.             {
  221.                 get { return (header.Area.Width * header.Area.Height); }
  222.             }
  223.  
  224.             /// <summary>
  225.             /// Creates a new sprite frame.
  226.             /// </summary>
  227.             /// <param name="frameHeader">Header data of the frame</param>
  228.             public Frame(SplFrameHeader frameHeader)
  229.             {
  230.                 header = frameHeader;
  231.                 data = null;
  232.                 texture = IntPtr.Zero;
  233.             }
  234.  
  235.             /*/// <summary>
  236.             /// Saves the frame as a bitmap file.
  237.             /// </summary>
  238.             /// <param name="filePath">A string the contains the name of the file to save this frame as.</param>
  239.             /// <param name="pxFormat">The pixel format of the bitmap to be saved.
  240.             /// Generally, for SPLN use Format24bppRgb and for SPL8 use Format16bppRgb565.</param>
  241.             public void Save(string filePath, System.Drawing.Imaging.PixelFormat pxFormat)
  242.             {
  243.                 // The bitmap will have had its width and height increased to be powers of 2
  244.                 // - we want to crop out only the image we are interested in
  245.                 Rectangle cropArea = new Rectangle(0, 0, header.Area.Width, header.Area.Height);
  246.                
  247.                 Bitmap bmp = this.Bitmap.Clone(cropArea, pxFormat);
  248.                 bmp.Save(filePath);
  249.             }*/
  250.         }
  251.         #endregion
  252.  
  253.         #region Fields
  254.         private bool isLoaded = false;      // True after loading a sprite
  255.         private string error;               // Used to store a description of the last error
  256.  
  257.         private SplHeader splHeader;
  258.         private Color[] palette;
  259.         private Frame[] frames;
  260.  
  261.         /// <summary>
  262.         /// Gets a message detailing the last failed operation.
  263.         /// </summary>
  264.         public string Error
  265.         {
  266.             get { return error; }
  267.         }
  268.  
  269.         /// <summary>
  270.         /// Gets a value indicating whether the object holds sprite data.
  271.         /// </summary>
  272.         public bool IsLoaded
  273.         {
  274.             get { return isLoaded; }
  275.         }
  276.  
  277.         /// <summary>
  278.         /// Gets the array of colours used by the sprite.  Only used with SPL8.
  279.         /// </summary>
  280.         public Color[] Palette
  281.         {
  282.             get { return palette; }
  283.         }
  284.  
  285.         /// <summary>
  286.         /// Gets the number of frames the sprite contains.
  287.         /// </summary>
  288.         public int FrameCount
  289.         {
  290.             get { return (int)splHeader.Frames; }
  291.         }
  292.  
  293.         /// <summary>
  294.         /// Gets a Frame object containing the image and data of a given frame in the sprite.
  295.         /// </summary>
  296.         /// <param name="index">ID of the frame to return.</param>
  297.         /// <returns>A Frame object containing the data held on a given frame.</returns>
  298.         public Frame this[int index]
  299.         {
  300.             get
  301.             {
  302.                 if (index >= 0 && index < splHeader.Frames)
  303.                     return frames[index];
  304.                 else
  305.                 {
  306.                     error = "Frame index " + index + " does not exist.";
  307.                     return new Frame();
  308.                 }
  309.             }
  310.         }
  311.         #endregion
  312.  
  313.         #region Constructors/Destructors
  314.         /// <summary>
  315.         /// Creates an empty sprite object.
  316.         /// </summary>
  317.         public Sprite() { }
  318.  
  319.         /// <summary>
  320.         /// Creates a sprite object from the given file path.
  321.         /// </summary>
  322.         /// <param name="d3d">Direct3D instance to use for textures.</param>
  323.         /// <param name="filePath">String containing complete file path of the sprite to load.</param>
  324.         public Sprite(ref Soma.DirectX.Direct3D d3d, string filePath)
  325.         {
  326.             Load(ref d3d, filePath);
  327.         }
  328.  
  329.         /// <summary>
  330.         /// Releases all memory held by the sprite.
  331.         /// </summary>
  332.         public void Release()
  333.         {
  334.             if (!IsLoaded)
  335.                 return;
  336.  
  337.             // Release from memory all textures held
  338.             for (int i = 0; i < splHeader.Frames; i++)
  339.             {
  340.                 if (frames[i].Texture != IntPtr.Zero)
  341.                     Soma.DirectX.Direct3D.ReleaseTexture(frames[i].Texture);
  342.             }
  343.  
  344.             isLoaded = false;
  345.         }
  346.  
  347.         /// <summary>
  348.         /// Destructor to make sure textures are released from memory.
  349.         /// </summary>
  350.         ~Sprite()
  351.         {
  352.             Release();
  353.         }
  354.         #endregion
  355.  
  356.         #region Public methods
  357.         /// <summary>
  358.         /// Loads a sprite from a file.
  359.         /// </summary>
  360.         /// <param name="d3d">Direct3D instance to use for textures.</param>
  361.         /// <param name="filePath">Full path to the file to load.</param>
  362.         /// <returns>True on operation success, false on failure.</returns>
  363.         public bool Load(ref Soma.DirectX.Direct3D d3d, string filePath)
  364.         {
  365.             isLoaded = false;
  366.  
  367.             byte[] fileData;        // Byte array to store the file
  368.  
  369.             try
  370.             {
  371.                 // Check for write-access in-case we don't have it as we will want to edit the file later.
  372.                 // Note: file-access permissions may change at any time - the best we can do is check we
  373.                 // have write-access now and offer a Save As option later.
  374.                 new FileIOPermission(FileIOPermissionAccess.Write, filePath);
  375.  
  376.                 // Read the file into a byte array
  377.                 fileData = File.ReadAllBytes(filePath);
  378.             }
  379.             catch (UnauthorizedAccessException ex)
  380.             {
  381.                 error = "Read/write access to sprite file is denied. (" + filePath + ")";
  382.                 return false;
  383.             }
  384.             catch (FileNotFoundException ex)
  385.             {
  386.                 error = "Sprite file could not be found at the specified location. (" + filePath + ")";
  387.                 return false;
  388.             }
  389.             catch (Exception ex)
  390.             {
  391.                 // For any other exceptions that may occur attempting to read from file
  392.                 error = "An error occured while opening sprite file. (" + filePath + ")";
  393.                 return false;
  394.             }
  395.  
  396.             // Get first 3 bytes - should be either SPL or BMP
  397.             string fileType = Encoding.UTF8.GetString(fileData, 0, 3);
  398.  
  399.             switch (fileType)
  400.             {
  401.                 case "SPL":
  402.                     if (LoadSPL(ref fileData))
  403.                     {
  404.                         GenerateTextures(ref d3d);
  405.                         return true;
  406.                     }
  407.                     else return false;
  408.  
  409.                 case "BMP":
  410.                     return LoadBMP(ref fileData);
  411.  
  412.                 default:
  413.                     error = "Unrecognized sprite type.";
  414.                     return false;
  415.             }
  416.  
  417.             // Unreachable
  418.         }
  419.  
  420.         /// <summary>
  421.         /// Creates/regenerates the textures for each frame.
  422.         /// </summary>
  423.         public void GenerateTextures(ref Soma.DirectX.Direct3D d3d)
  424.         {
  425.             if (!IsLoaded)
  426.                 return;
  427.  
  428.             for (int i = 0; i < splHeader.Frames; i++)
  429.             {
  430.                 if (frames[i].Texture != IntPtr.Zero)
  431.                 {
  432.                     // Delete from memory first
  433.                     Soma.DirectX.Direct3D.ReleaseTexture(frames[i].Texture);
  434.                 }
  435.  
  436.                 frames[i].Texture = (IntPtr)d3d.CreateTextureFromSplFrame(
  437.                                         frames[i].Header.Area.Width, frames[i].Header.Area.Height,
  438.                                         frames[i].ImageData, Palette, splHeader.IsCompressed);
  439.             }
  440.         }
  441.         #endregion
  442.  
  443.         #region Private methods
  444.         /// <summary>
  445.         /// Loads a Myth of Soma SPL-format sprite, such as SPL, MRF or
  446.         /// map OBM file types.
  447.         /// </summary>
  448.         /// <param name="fileData">Byte array containing entire file.</param>
  449.         /// <returns>True on load success, false on failure.</returns>
  450.         private bool LoadSPL(ref byte[] fileData)
  451.         {
  452.             // Current read-in location in the array
  453.             int offset = 0;
  454.  
  455.             // "SPLN" or "SPL8"
  456.             string type = Encoding.UTF8.GetString(fileData, offset, 4);
  457.             offset += 4;
  458.  
  459.             if (type != "SPLN" && type != "SPL8")
  460.             {
  461.                 error = "Attempt to load an unrecognized SPL format.";
  462.                 return false;
  463.             }
  464.  
  465.             // 128-length char array that may contain a short note on the file
  466.             string notes = Encoding.UTF8.GetString(fileData, offset, 128);
  467.             offset += 128;
  468.  
  469.             // Space intended for sprite's width and height (2 ints), but not used
  470.             offset += sizeof(uint) * 2;
  471.  
  472.             // Transparency colour (possibility these are in reverse order)
  473.             byte blue = fileData[offset]; offset++;
  474.             byte green = fileData[offset]; offset++;
  475.             byte red = fileData[offset]; offset++;
  476.             offset++; // RGBQUADs have a reserved bit following the RGB value
  477.  
  478.             Color transparency = Color.FromArgb((int)red, (int)green, (int)blue);
  479.  
  480.             // Number of frames in sprite
  481.             int frameQty = BitConverter.ToInt32(fileData, offset);
  482.             offset += sizeof(int);
  483.  
  484.             if (frameQty <= 0)
  485.             {
  486.                 error = "SPL file has an invalid number of frames (" + frameQty + ").";
  487.                 return false;
  488.             }
  489.  
  490.             // Create new SPL header
  491.             splHeader = new SplHeader(type, notes, transparency, (uint)frameQty);
  492.  
  493.             // Move offset ahead to where the data on the first frame begins
  494.             // Note that at this point offset == SplHeader.StructSize
  495.             offset = SplHeader.StructSize + (frameQty * 24);
  496.  
  497.             if (splHeader.IsCompressed)
  498.             {
  499.                 // Type SPL8 - read in the colour palette
  500.                 offset += 4;
  501.  
  502.                 // Colour count located at offset 148 + (frames * 24) + 4
  503.                 short colourCount = BitConverter.ToInt16(fileData, offset);
  504.                 offset += sizeof(short);
  505.  
  506.                 // Check the number of palette colours is ok
  507.                 if (colourCount <= 0 || colourCount > MaxPaletteSize)
  508.                 {
  509.                     error = "Sprite has an invalid number of colours in its palette ";
  510.                     error += "(" + colourCount + "/" + MaxPaletteSize + ").";
  511.                     return false;
  512.                 }
  513.  
  514.                 // Add space for the transparency colour if there is room
  515.                 palette = new Color[colourCount];
  516.  
  517.                 for (int i = 0; i < colourCount; i++)
  518.                 {
  519.                     // Read in the colour (RGB 565 format)
  520.                     ushort colour = BitConverter.ToUInt16(fileData, offset);
  521.                     offset += sizeof(ushort);
  522.  
  523.                     // Convert it to 32-bit (ARGB 8888 format)
  524.                     palette[i] = Soma.DirectX.Direct3D.ColorFrom16Bit(colour);
  525.                 }
  526.  
  527.                 // All SPL8s use the default transparency - make sure we use it
  528.                 splHeader.ColourKey = DefaultTransparency;
  529.             }
  530.  
  531.             // Create array to store all the frames
  532.             frames = new Frame[splHeader.Frames];
  533.  
  534.             // For every frame in the file, store its header data
  535.             for (uint i = 0; i < splHeader.Frames; i++)
  536.             {
  537.                 // Load in the area for this frame
  538.                 int left = BitConverter.ToInt32(fileData, offset); offset += sizeof(int);
  539.                 int top = BitConverter.ToInt32(fileData, offset); offset += sizeof(int);
  540.                 int right = BitConverter.ToInt32(fileData, offset); offset += sizeof(int);
  541.                 int bottom = BitConverter.ToInt32(fileData, offset); offset += sizeof(int);
  542.                
  543.                 Rectangle area = Rectangle.FromLTRB(left, top, right, bottom);
  544.  
  545.                 // The next int is unnecessary - it is the height of the frame which we already know
  546.                 offset += sizeof(int);
  547.  
  548.                 // Size of the frame in bytes
  549.                 int dataSize = BitConverter.ToInt32(fileData, offset);
  550.                 offset += sizeof(int);
  551.  
  552.                 // Check frame data is valid
  553.                 if (dataSize <= 0 || area.Width <= 0 || area.Height <= 0)
  554.                 {
  555.                     // File is invalid/corrupt and cannot be read from
  556.                     error = "Frame " + i + " of the SPL has invalid size data or is corrupt.";
  557.                     return false;
  558.                 }
  559.  
  560.                 // Unused int here
  561.                 offset += sizeof(int);
  562.  
  563.                 // Create a frame header from this data
  564.                 SplFrameHeader header = new SplFrameHeader(area, dataSize);
  565.  
  566.                 // Add frame header to frame array
  567.                 frames[i] = new Frame(header);
  568.             }
  569.  
  570.             // For every frame, load its data
  571.             for (int i = 0; i < splHeader.Frames; i++)
  572.             {
  573.                 // Copy frame's image data to Frame object
  574.                 frames[i].ImageData = new byte[frames[i].Header.DataSize];
  575.                 Buffer.BlockCopy(fileData, offset, frames[i].ImageData, 0, frames[i].Header.DataSize);
  576.                 offset += frames[i].Header.DataSize;
  577.             }
  578.  
  579.             // Success
  580.             isLoaded = true;
  581.  
  582.             return true;
  583.         }
  584.  
  585.  
  586.         /// <summary>
  587.         /// Loads a Myth of Soma BMP-format OBM sprite, such as those containing
  588.         /// images of inventory items.  Does not load actual .bmp files.
  589.         /// </summary>
  590.         /// <param name="fileData">Byte array containing entire file.</param>
  591.         /// <returns>True on operation success, false on failure.</returns>
  592.         private bool LoadBMP(ref byte[] fileData)
  593.         {
  594.             error = "Loading of OBM BMPs has not been implemented yet.";
  595.             return false;
  596.         }
  597.  
  598.  
  599.         /// <summary>
  600.         /// Converts the image data from an SPL frame into a Bitmap object stored in frames[frameId].
  601.         /// </summary>
  602.         /// <param name="frameId">ID of the frame to convert.</param>
  603.         /// <param name="palette">Array of colours the sprite uses if the sprite is in SPL8 format.
  604.         /// For SPLN type sprites, use null for this parameter.</param>
  605.         /// <returns>A Bitmap object containing the frame as an image.</returns>
  606.         private bool SplFrameToBmp(int frameId, Color[] palette)
  607.         {
  608.             if (frameId < 0 || frameId >= splHeader.Frames)
  609.             {
  610.                 error = "Attempt to convert non-existant frame: " + frameId;
  611.                 return false;
  612.             }
  613.             else if (frames[frameId].ImageData.Length <= 0)
  614.             {
  615.                 error = "Attempt to convert frame " + frameId + " but its ImageData is empty.";
  616.                 return false;
  617.             }
  618.  
  619.             int bmpWidth, bmpHeight, bmpTotalPixels;
  620.  
  621.             // The width and height of the bitmap should be powers of 2
  622.             // - not all graphics cards support textures that have dimensions that are not
  623.             // powers of 2 and result in wasted memory and stretched textures.
  624.             for (bmpWidth = 1; bmpWidth < frames[frameId].Header.Area.Width; bmpWidth *= 2) ;
  625.             for (bmpHeight = 1; bmpHeight < frames[frameId].Header.Area.Height; bmpHeight *= 2) ;
  626.             bmpTotalPixels = (bmpWidth * bmpHeight);
  627.  
  628.             Bitmap bmp = new Bitmap(bmpWidth, bmpHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  629.  
  630.             // Lock the bitmap's bits to edit the bitmap data directly
  631.             Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
  632.             System.Drawing.Imaging.BitmapData bmpData =
  633.                 bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
  634.                 bmp.PixelFormat);
  635.  
  636.             // Get the address of the first line of pixels
  637.             IntPtr ptrScan0 = bmpData.Scan0;
  638.  
  639.             // Declare an array to hold the bytes of the bitmap pixel data
  640.             int bmpLength  = bmpData.Stride * bmp.Height;
  641.             byte[] rgbValues = new byte[bmpLength];
  642.  
  643.             // Copy the RGB values into the array
  644.             System.Runtime.InteropServices.Marshal.Copy(ptrScan0, rgbValues, 0, bmpLength);
  645.  
  646.             int offset = 0;     // Byte offset from ImageData
  647.             short nodesInRow, pixelsToSkip, pixelsInNode;
  648.             ushort colour;
  649.  
  650.             // For calculating how many transparent pixels to draw before and after each node
  651.             int pxTransparentStart, pxTransparentEnd;
  652.  
  653.             // Loop to assign pixels to bitmap
  654.             for (int y = 0, x = 0; y < frames[frameId].Header.Area.Height; y++, x = 0)
  655.             {
  656.                 // Number of nodes on this row of the bitmap
  657.                 // - a node is a consecutive line of pixel data that contains no transparent pixels
  658.                 nodesInRow = BitConverter.ToInt16(frames[frameId].ImageData, offset);
  659.                 offset += sizeof(short);
  660.  
  661.                 for (int node = 0; node < nodesInRow; node++)
  662.                 {
  663.                     // How many pixels to skip before the node starts
  664.                     pixelsToSkip = BitConverter.ToInt16(frames[frameId].ImageData, offset);
  665.                     offset += sizeof(short);
  666.  
  667.                     // Make all pixels up to start of the node transparent
  668.                     pxTransparentStart = (y * bmpData.Stride) + (x * 3);
  669.                     pxTransparentEnd = pxTransparentStart + (pixelsToSkip * 3);
  670.                     for (int px = pxTransparentStart; px < pxTransparentEnd; )
  671.                     {
  672.                         rgbValues[px++] = splHeader.ColourKey.B;
  673.                         rgbValues[px++] = splHeader.ColourKey.G;
  674.                         rgbValues[px++] = splHeader.ColourKey.R;
  675.                         //rgbValues[px++] = byte.MaxValue;
  676.                     }
  677.  
  678.                     x += pixelsToSkip;
  679.  
  680.                     // How many pixels are in this node
  681.                     pixelsInNode = BitConverter.ToInt16(frames[frameId].ImageData, offset);
  682.                     offset += sizeof(short);
  683.  
  684.                     if ((x + pixelsInNode) > frames[frameId].Header.Area.Width)
  685.                     {
  686.                         // Number of pixels in this node is greater than the width of the image!
  687.                         error = "Number of pixels in node " + node + " of frame " + frameId
  688.                                 + " runs passed the width of the image.";
  689.                         return false;
  690.                     }
  691.  
  692.                     // Location to write pixel colour in the rgbValues array
  693.                     int pxLocation = (y * bmpData.Stride) + (x * 3);
  694.  
  695.                     // Read in each pixel in the node
  696.                     if (splHeader.IsCompressed)
  697.                     {
  698.                         for (int px = 0; px < pixelsInNode; px++)
  699.                         {
  700.                             // Colour of pixel is a reference to the palette
  701.                             rgbValues[pxLocation++] = palette[frames[frameId].ImageData[offset]].B;
  702.                             rgbValues[pxLocation++] = palette[frames[frameId].ImageData[offset]].G;
  703.                             rgbValues[pxLocation++] = palette[frames[frameId].ImageData[offset]].R;
  704.                             //rgbValues[pxLocation++] = byte.MaxValue;
  705.                            
  706.                             offset++;
  707.                             x++;
  708.                         }
  709.                     }
  710.                     else
  711.                     {
  712.                         Color pixel;
  713.  
  714.                         for (int px = 0; px < pixelsInNode; px++)
  715.                         {
  716.                             // Colour of the pixel is in RGB 565 format
  717.                             colour = BitConverter.ToUInt16(frames[frameId].ImageData, offset);
  718.                             offset += sizeof(ushort);
  719.  
  720.                             // Convert it into a .NET Color
  721.                             pixel = Soma.DirectX.Direct3D.ColorFrom16Bit(colour);
  722.  
  723.                             // Write it to array
  724.                             rgbValues[pxLocation++] = pixel.B;
  725.                             rgbValues[pxLocation++] = pixel.G;
  726.                             rgbValues[pxLocation++] = pixel.R;
  727.                             //rgbValues[pxLocation++] = byte.MaxValue;
  728.  
  729.                             x++;
  730.                         }
  731.                     }
  732.                 }
  733.  
  734.                 // Make all pixels after the nodes on this row transparent
  735.                 pxTransparentStart = (y * bmpData.Stride) + (x * 3);
  736.                 pxTransparentEnd = (y + 1) * bmpData.Stride;
  737.                 for (int px = pxTransparentStart; px < pxTransparentEnd; )
  738.                 {
  739.                     rgbValues[px++] = splHeader.ColourKey.B;
  740.                     rgbValues[px++] = splHeader.ColourKey.G;
  741.                     rgbValues[px++] = splHeader.ColourKey.R;
  742.                     //rgbValues[px++] = byte.MaxValue;
  743.                 }
  744.             }
  745.  
  746.             // Copy the RGB values back to the bitmap
  747.             System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptrScan0, bmpLength);
  748.  
  749.             // Unlock bitmap
  750.             bmp.UnlockBits(bmpData);
  751.  
  752.             // Success!
  753.             // this used to be here frames[frameId].Bitmap = bmp;
  754.             return true;
  755.         }
  756.         #endregion
  757.     }
  758. }