using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Xml;
using System.Xml.Serialization;
using System.Resources;
using System.IO;
namespace SpriteLibrary
{
internal struct ImageStruct
{
internal Image TheImage;
internal string ImageName;
}
///
/// Store Sprite information in a database. You can preload your database with sprite definitions, and then
/// create the sprites as needed. This can drastically reduce the initial load time of a game or something.
/// Though, what it really does is spread out the load time. It still takes the same amount of time to
/// load all the sprites, it just loads them on-demand. Using a dictionary often hides any load time issues.
///
public class SpriteDatabase
{
///
/// This is the list of SpriteInfo records that the database knows about. You can create your own list,
/// modify this list, or whatever. The database has some reasonable functions for loading and saving a
/// sprite database.
///
public List SpriteInfoList = new List();
List TheImages = new List();
ResourceManager myResourceManager = null;
string Filename = "";
Size SnapGridSize = new Size(5, 5);
///
/// The sprite database instantiation function. The filename can either be a file on the computer or it
/// can be the string name of a resource (the filename without the extension. If your file is accessed
/// by Properties.Resources.MySprites, the "filename" would be "MySprites")
///
/// The ResourceManager for your project. Usually
/// Properties.Resources.ResourceManager
/// Either a path and file (like: @"c:\users\me\Desktop\myfile.xml") or
/// the name of a resource (like: "myfile")
public SpriteDatabase(ResourceManager theResourceManager, string filename)
{
myResourceManager = theResourceManager;
Filename = filename;
Load();
}
internal void Load()
{
LoadSpriteInfo();
}
internal ResourceManager GetResourceManager()
{
return myResourceManager;
}
///
/// Tell the database to save the sprite definitions. Use this while you are creating your game.
/// When you are done, you will usually want to take your sprite definition file and add it to the
/// resources of your game. The resources cannot be saved to, so you cannot continue to add new sprites
/// once you are loading and saving them from a resources file. But, the resources file is included with
/// the program when you build it.
///
public void Save()
{
if(!DoesResourceExist(Filename))
{
//we will try to save it as a file
try
{
WriteToXmlFile>(Filename, SpriteInfoList);
}
catch (Exception e)
{
throw new Exception("SpriteDatabase failed to save: Filename:" + Filename +"\n" + "ERROR: " + e.ToString(), e);
}
}
}
public void SetSnapGridSize(Size GridSize)
{
if (GridSize.Width <= 0) return;
if (GridSize.Height <= 0) return;
if (GridSize.Width > 500) return;
if (GridSize.Height > 500) return;
SnapGridSize = GridSize;
}
//*******************************
//**** Sprite Info Functions ***
//*******************************
#region SpriteInfo Functions
void LoadSpriteInfo()
{
if (DoesResourceExist(Filename))
{
//This clears out the old list, as it gets replaced.
SpriteInfoList = LoadObjectFromXmlFile>(Filename, myResourceManager);
}
else
{
//try loading it from an actual filename
if (File.Exists(Filename))
SpriteInfoList = ReadFromXmlFile>(Filename);
}
//If neither works, we end up with an empty file.
//If it fails, SpriteInfoList is null and things explode.
if (SpriteInfoList == null)
SpriteInfoList = new List(); //make an empty one so things do not explode.
}
public List SpriteNames()
{
List theNames = new List();
foreach (SpriteInfo si in SpriteInfoList)
{
theNames.Add(si.SpriteName);
}
return theNames;
}
internal bool DoesResourceExist(string resourcename)
{
if (myResourceManager == null) return false;
if (myResourceManager.GetObject(resourcename) != null)
return true;
return false;
}
public void OpenEditWindow(int FirstItemIndex=-1)
{
SpriteEntryForm SEF = new SpriteEntryForm(this, SpriteInfoList, SnapGridSize);
SEF.ShowDialog();
//Use the updated list returned from the form.
SpriteInfoList.Clear();
SpriteInfoList.AddRange(SEF.GetUpdatedList());
}
#endregion
#region General Functions
public Image GetImageFromName(string Name, bool UseSmartImages)
{
Image MyImage = null;
if (UseSmartImages)
{
foreach (ImageStruct IS in TheImages)
{
if (IS.ImageName.Equals(Name, StringComparison.InvariantCultureIgnoreCase))
{
MyImage = IS.TheImage;
break;
}
}
}
if (MyImage == null)
{
ResourceManager rm = myResourceManager;
MyImage = (Bitmap)rm.GetObject(Name);
if (UseSmartImages)
{
ImageStruct NewIS = new ImageStruct();
NewIS.ImageName = Name;
NewIS.TheImage = MyImage;
TheImages.Add(NewIS);
}
}
return MyImage;
}
public Sprite SmartDuplicateSprite(SpriteController theController, string SpriteName, bool UseSmartImages = true)
{
Sprite DestSprite = theController.DuplicateSprite(SpriteName);
if (DestSprite != null) return DestSprite;
//If it does not exist, make it
foreach (SpriteInfo SI in SpriteInfoList)
{
if (SI.SpriteName == SpriteName)
{
SI.CreateSprite(theController, this);
return theController.DuplicateSprite(SpriteName);
}
}
return null;
}
#endregion
#region Generic XML Funcs
///
/// Load in an XML serialized item from the specified ResourceManager. You will usually make one of these by
/// creating an object and using SpriteDatabase.WriteToXmlFile to
/// save it to a file on your desktop. Then you can drag and drop that file into your project and then use this
/// LoadObjectFromXmlFile function.
///
/// The type of object to load. It could be something as simple as an int, a class, or a list of classes.
/// The resource item to load. If you would access it like: properties.resources.myFile,
/// the correct value to put here would be "myFile"
/// The resource manager. Usually Properties.Resources.ResourceManager
/// An object of the value you specified. Or null if it fails.
public static T LoadObjectFromXmlFile(string XMLResourceToLoad, ResourceManager MyManager) where T : new()
{
//Load in the sprite data
XmlSerializer serializer = new XmlSerializer(typeof(T));
// Retrieves String and Image resources.
object titem = MyManager.GetObject(XMLResourceToLoad);
byte[] item = (byte[])System.Text.Encoding.UTF8.GetBytes((string)titem);
try
{
return (T)serializer.Deserialize(new MemoryStream(item));
}
finally
{
}
}
///
/// Writes the given object instance to an XML file.
/// Only Public properties and variables will be written to the file. These can be any type though, even other classes.
/// If there are public properties/variables that you do not want written to the file, decorate them with the [XmlIgnore] attribute.
/// Object type must have a parameterless constructor.
///
/// The type of object being written to the file.
/// The file path to write the object instance to.
/// The object instance to write to the file.
internal static void WriteToXmlFile(string filePath, T objectToWrite) where T : new()
{
TextWriter writer = null;
try
{
var serializer = new XmlSerializer(typeof(T));
writer = new StreamWriter(filePath);
serializer.Serialize(writer, objectToWrite);
}
finally
{
if (writer != null)
writer.Close();
}
}
///
/// Reads an object instance from an XML file.
/// Object type must have a parameterless constructor.
///
/// The type of object to read from the file.
/// The file path to read the object instance from.
/// Returns a new instance of the object read from the XML file.
public static T ReadFromXmlFile(string filePath) where T : new()
{
TextReader reader = null;
try
{
var serializer = new XmlSerializer(typeof(T));
reader = new StreamReader(filePath);
return (T)serializer.Deserialize(reader);
}
finally
{
if (reader != null)
reader.Close();
}
}
public static string WriteToXMLString(T toSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}
}
public static T ReadFromXmlString(string toDeserialize) where T : new()
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StringReader textReader = new StringReader(toDeserialize))
return (T)xmlSerializer.Deserialize(textReader);
}
public static T CloneByXMLSerializing(T ObjectToClone)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
string dest;
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, ObjectToClone);
dest = textWriter.ToString();
}
using (StringReader textReader = new StringReader(dest))
return (T)xmlSerializer.Deserialize(textReader);
}
#endregion
}
}