diff --git a/Game1.cs b/Game1.cs new file mode 100644 index 0000000..4044956 --- /dev/null +++ b/Game1.cs @@ -0,0 +1,134 @@ +#region Using Statements +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using Microsoft.Xna.Framework.Storage; +using Microsoft.Xna.Framework.GamerServices; +using System.Diagnostics; +#endregion + +namespace MonoGameSaveManager +{ + /// + /// A basic game to test out the SaveManager functionality. + /// Just follow along with the comments. + /// + public class Game1 : Game + { + GraphicsDeviceManager graphics; + SpriteBatch spriteBatch; + + SaveManager save; + bool saved = false; + + public Game1() + : base() + { + graphics = new GraphicsDeviceManager(this); + Content.RootDirectory = "Content"; + + string saveFolder = "SaveManagerTest"; // put your save folder name here + string saveFile = "test.sav"; // put your save file name here + + // Pick one: + + // - this will use the XNA StorageDevice method. + save = new StorageDeviceSaveManager(saveFolder, saveFile, PlayerIndex.One); + + // - this will use the .NET IsolatedStorage method. + //save = new IsolatedStorageSaveManager(saveFolder, saveFile); + } + + /// + /// Allows the game to perform any initialization it needs to before starting to run. + /// This is where it can query for any required services and load any non-graphic + /// related content. Calling base.Initialize will enumerate through any components + /// and initialize them as well. + /// + protected override void Initialize() + { + // TODO: Add your initialization logic here + + base.Initialize(); + } + + /// + /// LoadContent will be called once per game and is the place to load + /// all of your content. + /// + protected override void LoadContent() + { + // Create a new SpriteBatch, which can be used to draw textures. + spriteBatch = new SpriteBatch(GraphicsDevice); + + // TODO: use this.Content to load your game content here + } + + /// + /// UnloadContent will be called once per game and is the place to unload + /// all content. + /// + protected override void UnloadContent() + { + // TODO: Unload any non ContentManager content here + } + + /// + /// Allows the game to run logic such as updating the world, + /// checking for collisions, gathering input, and playing audio. + /// + /// Provides a snapshot of timing values. + protected override void Update(GameTime gameTime) + { + if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) + Exit(); + + // test save stuff + // since this is Update, i don't want it to keep saving every tick + if (!saved) + { + saved = true; + + // let's make up some save data + save.Data.testInt = 434; + save.Data.testBool = false; + save.Data.testString = "wow a test"; + + // save it + save.Save(); + + // erase data so we can check if loading works + save.Data = new SaveData(); + + // load it back + save.Load(); + + // did it work? let's hope so! + // this will show up in Visual Studio's Output window when running. + // you can also just use a Breakpoint or whatever. + Debug.WriteLine("final save data:"); + Debug.WriteLine("testInt = " + save.Data.testInt); + Debug.WriteLine("testBool = " + save.Data.testBool); + Debug.WriteLine("testString = " + save.Data.testString); + } + + base.Update(gameTime); + } + + /// + /// This is called when the game should draw itself. + /// + /// Provides a snapshot of timing values. + protected override void Draw(GameTime gameTime) + { + GraphicsDevice.Clear(Color.CornflowerBlue); + + // TODO: Add your drawing code here + + base.Draw(gameTime); + } + } +} diff --git a/IsolatedStorageSaveManager.cs b/IsolatedStorageSaveManager.cs new file mode 100644 index 0000000..0ee9943 --- /dev/null +++ b/IsolatedStorageSaveManager.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.IsolatedStorage; +using System.Linq; +using System.Text; +using System.Xml.Serialization; + +namespace MonoGameSaveManager +{ + /// + /// Uses the .NET IsolatedStorage to load/save game data. + /// Saves end up on "C:\Users\...\AppData\Local\IsolatedStorage\..." on Windows. + /// + public class IsolatedStorageSaveManager : SaveManager + { + /// + /// /// + /// Creates a new save game manager based on .NET IsolatedStorage. + /// + /// Name of the folder containing the save. + /// Name of the save file. + public IsolatedStorageSaveManager(string folderName, string fileName) + : base(folderName, fileName) + { } + + public override void Load() + { + using (IsolatedStorageFile isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null)) + { + // Ignore if directory doesn't exist. + if (!isf.DirectoryExists(folderName)) + return; + + string filePath = Path.Combine(folderName, fileName); + + // Ignore if save doesn't exist. + if (!isf.FileExists(filePath)) + return; + + // Open the save file. + using (IsolatedStorageFileStream stream = isf.OpenFile(filePath, FileMode.Open)) + { + // Get the XML data from the stream and convert it to object. + XmlSerializer serializer = new XmlSerializer(typeof(SaveData)); + Data = (SaveData)serializer.Deserialize(stream); + } + } + } + + public override void Save() + { + using (IsolatedStorageFile isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null)) + { + // Create directory if it doesn't exist. + if (!isf.DirectoryExists(folderName)) + isf.CreateDirectory(folderName); + + string filePath = Path.Combine(folderName, fileName); + + // Create new save file. + using (IsolatedStorageFileStream stream = isf.CreateFile(filePath)) + { + // Convert the object to XML data and put it in the stream. + XmlSerializer serializer = new XmlSerializer(typeof(SaveData)); + serializer.Serialize(stream, Data); + } + } + } + } +} diff --git a/SaveData.cs b/SaveData.cs new file mode 100644 index 0000000..32fe8da --- /dev/null +++ b/SaveData.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MonoGameSaveManager +{ + /// + /// Container for your save game data. + /// Put the variables you need here, as long as it's serializable. + /// + [Serializable] + public class SaveData + { + public int testInt; + public bool testBool; + public string testString; + } +} diff --git a/SaveManager.cs b/SaveManager.cs new file mode 100644 index 0000000..5fbea59 --- /dev/null +++ b/SaveManager.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MonoGameSaveManager +{ + /// + /// Manages a save file for you. + /// + public abstract class SaveManager + { + protected string folderName, fileName; + + /// + /// Access save game data. + /// + public SaveData Data { get; set; } + + /// + /// Creates a new save game manager. + /// + /// Name of the folder containing the save. + /// Name of the save file. + public SaveManager(string folderName, string fileName) + { + this.folderName = folderName; + this.fileName = fileName; + this.Data = new SaveData(); + } + + /// + /// Loads the data from disk to memory. + /// + public abstract void Load(); + + /// + /// Saves the data in memory to disk. + /// + public abstract void Save(); + } +} diff --git a/StorageDeviceSaveManager.cs b/StorageDeviceSaveManager.cs new file mode 100644 index 0000000..aab390a --- /dev/null +++ b/StorageDeviceSaveManager.cs @@ -0,0 +1,100 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Storage; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml.Serialization; + +namespace MonoGameSaveManager +{ + /// + /// Uses the XNA StorageDevice to load/save game data. + /// Saves end up on "C:\Users\...\Documents\SavedGames\..." on Windows. + /// + public class StorageDeviceSaveManager : SaveManager + { + private PlayerIndex? player; + + /// + /// Creates a new save game manager based on XNA StorageDevice. + /// + /// Name of the folder containing the save. + /// Name of the save file. + /// Player the save belongs to, or null for a global save. + public StorageDeviceSaveManager(string folderName, string fileName, PlayerIndex? player) + : base(folderName, fileName) + { + this.player = player; + } + + private StorageDevice getStorageDevice() + { + IAsyncResult result; + // Get a global folder. + if (player == null) + result = StorageDevice.BeginShowSelector(null, null); + // Get a player-specific subfolder. + else + result = StorageDevice.BeginShowSelector((PlayerIndex)player, null, null); + + result.AsyncWaitHandle.WaitOne(); + StorageDevice device = StorageDevice.EndShowSelector(result); + result.AsyncWaitHandle.Close(); + return device; + } + + public override void Load() + { + // Open a storage device. + StorageDevice device = getStorageDevice(); + + // Open a storage container. + IAsyncResult result = device.BeginOpenContainer(folderName, null, null); + result.AsyncWaitHandle.WaitOne(); + using (StorageContainer container = device.EndOpenContainer(result)) + { + result.AsyncWaitHandle.Close(); + + // Ignore if save doesn't exist. + if (!container.FileExists(fileName)) + return; + + // Open the save file. + using (Stream stream = container.OpenFile(fileName, FileMode.Open)) + { + // Get the XML data from the stream and convert it to object. + XmlSerializer serializer = new XmlSerializer(typeof(SaveData)); + Data = (SaveData)serializer.Deserialize(stream); + } + } + } + + public override void Save() + { + // Open a storage device. + StorageDevice device = getStorageDevice(); + + // Open a storage container. + IAsyncResult resultStorage = device.BeginOpenContainer(folderName, null, null); + resultStorage.AsyncWaitHandle.WaitOne(); + using (StorageContainer container = device.EndOpenContainer(resultStorage)) + { + resultStorage.AsyncWaitHandle.Close(); + + // Delete old save file. + if (container.FileExists(fileName)) + container.DeleteFile(fileName); + + // Create new save file. + using (Stream stream = container.CreateFile(fileName)) + { + // Convert the object to XML data and put it in the stream. + XmlSerializer serializer = new XmlSerializer(typeof(SaveData)); + serializer.Serialize(stream, Data); + } + } + } + } +}