using System; using System.IO; namespace Framework.Storage.Containers { /// XML-backed persistent state container /// Type of state the persistent container stores internal class XmlBackedContainer : IVerifiedPersistentContainer where TState : class, TInterface, new() where TInterface : class { #if false /// Triggered when the settings should be reapplied /// /// /// Typically, you don't want to tightly integrate all your character controllers /// and other game logic with the persistent state saving system and instead /// create 'glue' objects which take the settings from a persistent container /// and apply them at will. /// /// /// This event can be used to signal to those 'glue' objects that they should /// apply the current settings to the game's controllers again. /// /// /// One danger of this event is that dead objects may keep hanging on to it. /// Any 'glue' component needs to cleanly unsubscribe when it is being destroyed! /// /// public event Action Apply; #endif /// Initializes a new XML-backed persistent state container /// Directory in which state file will be stored /// Default name of the state file public XmlBackedContainer(string directory, string defaultFilename) { this.state = new TState(); this.directory = directory; this.defaultFilename = defaultFilename; this.isGenuine = true; } /// Accesses the informations stored in the state public TInterface Access() { return this.state; } /// Accesses the game-specific state /// /// Game-specific type of state the game is storing /// /// The game-specific state public TSpecificState Access() where TSpecificState : TInterface { object stateAsObject = (object)this.state; return (TSpecificState)stateAsObject; // return default(TSpecificState); // This should work. Cast unknown class X to Y... } /// Optional custom serializer for the state, null to use default public IPersistentStateSerializer CustomSerializer { get { return this.customSerializer; } set { this.customSerializer = value; } } /// Loads the saved state of the container from disk public void Load() { // If we don't do this and a future version of the game adds new attributes // or properties, the player could mix things up by playing a save with // the desired attributes, then loading an old-version save (leaving those // attributes in their previous states). Reset(); if(this.customSerializer == null) { loadUsingDefaultSerializer(); this.isGenuine = true; } else { this.isGenuine = loadUsingCustomSerializer(); } } /// Saves the current state of the container to disk public void Save() { if(this.customSerializer == null) { saveUsingDefaultSerializer(); } else { saveUsingCustomSerializer(); } } /// Resets the container to its default settings public void Reset() { // Would like to replace this with create-instance-and-copy-into-existing this.state = new TState(); } /// Whether the container's state is tamper-free /// > /// If the user tries to edit the save file, a verified container will /// detect this and report its state as non-genuine. /// public bool IsGenuine { get { return this.isGenuine; } } /// Updates the Directory in which the XML file will be stored /// New directory to use for the XML file public void ChangeDirectory(string newDirectory) { this.directory = newDirectory; } /// Loads the state using a custom serializer private bool loadUsingCustomSerializer() { string path = this.customSerializer.DefaultFilename; if(string.IsNullOrEmpty(path)) { path = Path.Combine(this.directory, this.defaultFilename); } else { path = Path.Combine(this.directory, path); } if(File.Exists(path)) { return this.customSerializer.Load(this.state, path); } else { Reset(); return true; } } /// Loads the state using the default XML serializer private void loadUsingDefaultSerializer() { if(!Directory.Exists(this.directory)) { Directory.CreateDirectory(this.directory); } string path = Path.Combine(this.directory, this.defaultFilename); if(File.Exists(path)) { SimpleXmlSerializer.Load(this.state, path); } else { Reset(); } } /// Saves the state using a custom serializer private void saveUsingCustomSerializer() { if(!Directory.Exists(this.directory)) { Directory.CreateDirectory(this.directory); } string path = this.customSerializer.DefaultFilename; if(string.IsNullOrEmpty(path)) { path = Path.Combine(this.directory, this.defaultFilename); } else { path = Path.Combine(this.directory, path); } this.customSerializer.Save(this.state, path); } /// Saves the state using the default XML serializer private void saveUsingDefaultSerializer() { if(!Directory.Exists(this.directory)) { Directory.CreateDirectory(this.directory); } string path = Path.Combine(this.directory, this.defaultFilename); SimpleXmlSerializer.Save(this.state, path); } /// Custom serializer that will be used to persist the container private IPersistentStateSerializer customSerializer; /// Informations stored in this container private TState state; /// Directory in which the state file will be saved private string directory; /// Default filename that will be used for the state file /// /// This can be overriden if a custom serializer is assigned /// private string defaultFilename; /// Whether the current state is tamper-free private bool isGenuine; } } // namespace Framework.Storage.Containers