// Retrieves image properties given an XML file.
 public static ImageProperties GetImageProperties(string xmldata, string name, string path)
 {
     if (xmldata != null && xmldata.Length > 0)
     {
         //Create the output structure (empty)
         ImageProperties output = new ImageProperties();
         //Convert file data into XML format for easy property access.
         XmlDocument xhr = new XmlDocument();
         xhr.LoadXml(xmldata);
         //Only count from the first found Image Properties Element.
         XmlNodeList temp = xhr.GetElementsByTagName("IMAGE_PROPERTIES");
         if (temp.Count > 0)
         {
             //Fill the output structure.
             output.name = name;
             output.path = path;
             output.width = System.Convert.ToInt32(temp[0].Attributes.GetNamedItem("WIDTH").Value);
             output.height = System.Convert.ToInt32(temp[0].Attributes.GetNamedItem("HEIGHT").Value);
             output.numImages = System.Convert.ToInt32(temp[0].Attributes.GetNamedItem("NUMIMAGES").Value);
             output.numTiles = System.Convert.ToInt32(temp[0].Attributes.GetNamedItem("NUMTILES").Value);
             output.version = System.Convert.ToSingle(temp[0].Attributes.GetNamedItem("VERSION").Value);
             output.tileSize = System.Convert.ToInt32(temp[0].Attributes.GetNamedItem("TILESIZE").Value);
         }
         return output;
     }
     return new ImageProperties();
 }
        // Builds an image from multiple sources.
        static Bitmap assembleImage(ImageProperties imageData)
        {
            //Create a canvas ( image array )
            Bitmap output = new Bitmap(imageData.width, imageData.height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            int zoom = (imageData.width > imageData.height) ? findZoom(imageData.width) : findZoom(imageData.height);

            int nbrTilesX = (int)Math.Ceiling((float)imageData.width / (float)imageData.tileSize);
            int nbrTilesY = (int)Math.Ceiling((float)imageData.height / (float)imageData.tileSize);
            int loaded = 0;
            int totalTiles = nbrTilesX * nbrTilesY;
            int skippedTiles = imageData.numTiles - totalTiles;

            int tileGroup = (int) Math.Floor( (float)skippedTiles / 256 ); // Original script divides by 256. Perhaps it refers to the tile size?
            int tileGroupCounter = (int)((float)skippedTiles % 256); // Original script divides by 256. Perhaps it refers to the tile size?

            int x, y;
            for (y = 0; y < nbrTilesY; y++)
            {
                for (x = 0; x < nbrTilesX; x++)
                {

                    if (tileGroupCounter >= 256)
                    {
                        tileGroup++;
                        tileGroupCounter = 0;
                    }

                    tileGroupCounter++;
                    string url = imageData.path + "/TileGroup" + tileGroup + "/" + zoom + "-" + x + "-" + y + ".jpg";
                    HttpStatusCode result = FileStatus(url);
                    try
                    {
                        //Try all zoom levels under the current.
                        while( result != HttpStatusCode.OK && zoom > 0)
                        {
                            if (result == HttpStatusCode.NotFound)
                            {
                                zoom--;
                                url = imageData.path + "/TileGroup" + tileGroup + "/" + zoom + "-" + x + "-" + y + ".jpg";
                                result = FileStatus(url);
                            }
                            else
                            {
                                Console.WriteLine("Unexpected server error : " + FileStatus(url).ToString());
                            }
                        }
                        //Add a tile image to the canvas.
                        if (result == HttpStatusCode.OK)
                        {
                            output = OverlayImage(output, GetTile(url), x * imageData.tileSize, y * imageData.tileSize);
                        }
                    }
                    catch
                    {
                        Console.WriteLine("I don't know what happened.");
                    }

                }
            }
            //Return image.
            return output;
        }
        // Retrieves the urls of all images used in the maximum level zoomify viewer.
        public static string[] GetImageList(ImageProperties imageData)
        {
            List<string> output = new List<string>();
            int zoom = (imageData.width > imageData.height) ? findZoom(imageData.width) : findZoom(imageData.height);
            int nbrTilesX = (int)Math.Ceiling((float)imageData.width / (float)imageData.tileSize);
            int nbrTilesY = (int)Math.Ceiling((float)imageData.height / (float)imageData.tileSize);
            int totalTiles = nbrTilesX * nbrTilesY;
            int skippedTiles = imageData.numTiles - totalTiles;

            int tileGroup = (int) Math.Floor( (float)skippedTiles / 256 ); // Original script divides by 256. Perhaps it refers to the tile size?
            int tileGroupCounter = (int)((float)skippedTiles % 256); // Original script divides by 256. Perhaps it refers to the tile size?

            int x, y;
            for (y = 0; y < nbrTilesY; y++)
            {
                for (x = 0; x < nbrTilesX; x++)
                {

                    if (tileGroupCounter >= 256)
                    {
                        tileGroup++;
                        tileGroupCounter = 0;
                    }

                    tileGroupCounter++;

                    output.Add(imageData.path + "/TileGroup" + tileGroup + "/" + zoom + "-" + x + "-" + y + ".jpg");
                }
            }
            return output.ToArray();
        }