public MemoryStream SaveToStream()
        {
            MemoryStream stream = null;
            string       json   = m_saveData.ToJSON();

            //Compress the data using LZF compression
            byte[] compressed = null;
            using (MemoryStream compMemStream = new MemoryStream())
            {
                using (StreamWriter writer = new StreamWriter(compMemStream, Encoding.UTF8))
                {
                    writer.Write(json);
                    writer.Close();
                    compressed = CLZF2.Compress(compMemStream.ToArray());
                }
            }
            if (compressed != null)
            {
                byte[] encrypted = AESEncryptor.Encrypt(m_cryptoKey, m_cryptoIV, compressed);

                if (encrypted != null)
                {
                    stream = new MemoryStream();
                    //Write version and headers!
                    byte[] version = SaveUtilities.SerializeVersion(HeaderVersion);
                    stream.Write(version, 0, version.Length);
                    byte[] header = SaveUtilities.SerializeHeader(m_modifiedTime, m_deviceName, SaveUtilities.CalculateMD5Hash(encrypted), encrypted.Length);
                    stream.Write(header, 0, header.Length);
                    //Write encrypted and compressed data to stream
                    stream.Write(encrypted, 0, encrypted.Length);
                }
            }
            return(stream);
        }
        private bool IsValidFile(Stream stream, ref byte[] contentBytes)
        {
            bool valid = false;

            string md5Hash       = null;
            int    headerLength  = 0;
            int    contentLength = 0;
            bool   validHeader   = SaveUtilities.DeserializeHeader(stream, ref headerLength, ref m_modifiedTime, ref m_deviceName, ref md5Hash, ref contentLength);

            if (validHeader)
            {
                contentBytes = new byte[contentLength];
                int contentRead = stream.Read(contentBytes, 0, contentLength);

                if (contentRead == contentLength)
                {
                    string contentHash = SaveUtilities.CalculateMD5Hash(contentBytes);

                    if (md5Hash == contentHash)
                    {
                        valid = true;
                    }
                    else
                    {
                        Debug.LogError(string.Format("SaveData :: MD5 Checksum Failed (Got {0} expected {1})", md5Hash, contentHash));
                    }
                }
                else
                {
                    Debug.LogError(string.Format("SaveData :: Content length different to expected (Got {0} expected {1})", contentRead, contentLength));
                }
            }
            else
            {
                Debug.LogError("SaveData :: Invalid header detected!");
            }

            return(valid);
        }
        private LoadState LoadSave(User user, out SaveData loadedData)
        {
            m_savingEnabled = false;

            string           saveID   = !string.IsNullOrEmpty(user.saveID) ? user.saveID : LocalSaveID;
            SaveData         save     = new SaveData(saveID);
            Func <LoadState> loadSave = null;

            loadSave = delegate()
            {
                LoadState loadResult = save.Load();

                switch (loadResult)
                {
                case LoadState.NotFound:
                    if (save.Key != LocalSaveID)
                    {
                        Debug.Log("SaveGameManager (LoadSave) :: Haven't found save for saveID - " + saveID + " attempting to load local.sav instead!");

                        //If the save isn't found and we aren't trying to load a local save already then try to load a local save instead
                        save = new SaveData(LocalSaveID);

                        loadResult = loadSave();

                        if (loadResult == LoadState.OK)
                        {
                            Debug.Log("SaveGameManager (LoadSave) :: Found local.sav! Converting to user save");

                            save.UpdateSavePathAndKey(SaveUtilities.GetSavePath(saveID), saveID);

                            SaveState state = save.Save();

                            //Note: possible save duplication exploit

                            if (state == SaveState.OK)
                            {
                                //Delete old local.sav when done!
                                string localSavePath = SaveUtilities.GetSavePath(LocalSaveID);

                                try
                                {
                                    if (File.Exists(localSavePath))
                                    {
                                        File.Delete(localSavePath);
                                    }

                                    // Fix for HSW-5647 - delete the backup file as well as otherwise in some cases user can get a corrupted popup
                                    string backupPath = localSavePath + ".backup";
                                    if (File.Exists(backupPath))
                                    {
                                        File.Delete(backupPath);
                                    }
                                }
                                catch (Exception e)
                                {
                                    Debug.LogWarning("SaveGameManager (LoadSave) :: Unable to delete " + localSavePath + " - " + e.Message);
                                }
                            }
                        }
                    }
                    break;
                }
                return(loadResult);
            };

            LoadState result      = loadSave();
            Action    loadSystems = delegate()
            {
                if (result == LoadState.OK)
                {
                    result = LoadSystems(save);

                    if (result == LoadState.OK)
                    {
                        m_savingEnabled = true;
                    }
                }
            };

            //Check for valid results and enable saving if we are in a valid state!
            switch (result)
            {
            case LoadState.OK:
                //Now need to check game systems can load it!
                bool upgraded = false;
                result       = UpgradeSystems(save, out upgraded);
                save.Version = m_version;
                //TODO only save on upgrade
                if (result == LoadState.OK && upgraded)
                {
                    SaveToDisk();
                }
                loadSystems();
                break;

            case LoadState.NotFound:
                Debug.Log("SaveGameManager (LoadSave) :: No save found! Creating new save!");
                //Create a new save
                result = LoadState.OK;
                loadSystems();
                save.Version = m_version;
                if (result == LoadState.OK)
                {
                    save.Save();
                }
                break;

            default:
                loadSystems();
                m_savingEnabled = false;
                break;
            }
            loadedData = save;
            return(result);
        }
 public SaveData(string key) : this(SaveUtilities.GetSavePath(key), key)
 {
 }
        public LoadState LoadFromStream(Stream stream)
        {
            LoadState state = LoadState.Corrupted;

            try
            {
                byte[] decompressed = null;
                byte[] contentBytes = null;
                bool   versionOkay  = false;
                //Check version first
                int headerVersion = SaveUtilities.DeserializeVersion(stream);
                if (headerVersion == -1)
                {
                    state = LoadState.Corrupted;
                }
                else if (headerVersion < HeaderVersion)
                {
                    stream = UpgradeFile(stream);

                    if (stream != null)
                    {
                        versionOkay = true;
                    }
                    else
                    {
                        state = LoadState.Corrupted;
                    }
                }
                else if (headerVersion > HeaderVersion)
                {
                    state = LoadState.VersionMismatch;
                }
                else
                {
                    versionOkay = true;
                }

                if (versionOkay)
                {
                    if (IsValidFile(stream, ref contentBytes))
                    {
                        byte[] decrypted = AESEncryptor.Decrypt(m_cryptoKey, m_cryptoIV, contentBytes);

                        if (decrypted != null)
                        {
                            decompressed = CLZF2.Decompress(decrypted);
                        }
                        else
                        {
                            Debug.LogError("SaveData :: Decryption failed!");
                            state = LoadState.Corrupted;
                        }
                    }
                    else
                    {
                        Debug.LogError("SaveData :: File Corrupted!");
                        state = LoadState.Corrupted;
                    }
                }

                if (decompressed != null)
                {
                    using (MemoryStream jsonMemoryStream = new MemoryStream(decompressed))
                    {
                        using (StreamReader reader = new StreamReader(jsonMemoryStream))
                        {
                            if (m_saveData.FromJSON(reader))
                            {
                                state = LoadState.OK;
                            }
                            else
                            {
                                Debug.LogWarning("Trying to load invalid JSON file at path: " + m_savePath);
                                state = LoadState.Corrupted;
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                //TODO determine exception types and see if any are recoverable!
                Debug.LogWarning("Exception when parsing file from stream");
                Debug.LogWarning(e);
            }
            return(state);
        }
        public LoadState Load()
        {
            LoadState state    = LoadState.NotFound;
            string    savePath = null;

            if (File.Exists(m_savePath))
            {
                savePath = m_savePath;
            }
            else if (File.Exists(m_saveTempPath))
            {
                savePath = m_saveTempPath;
            }
            else if (File.Exists(m_saveBackupPath))
            {
                savePath = m_saveBackupPath;
            }

            if (savePath != null)
            {
                try
                {
                    using (MemoryStream ms = SaveUtilities.GetUnpaddedSaveData(savePath))
                    {
                        state = LoadFromStream(ms);
                    }
                }
                catch (UnauthorizedAccessException e)
                {
                    Debug.LogError("Permissions error when loading file at path: " + savePath);
                    Debug.LogException(e);
                    state = LoadState.PermissionError;
                }
                catch (FileNotFoundException)
                {
                    Debug.LogWarning("No save file found at path: " + savePath);
                }
                catch (Exception e)
                {
                    Debug.LogError("Exception when loading file at path: " + savePath);
                    Debug.LogException(e);
                    state = LoadState.Corrupted;
                }
            }
            else
            {
                Debug.LogWarning("No save file found at path: " + savePath);
            }

            //If we can't load from disk try player prefs! Player prefs will be set if we detected an issue when saving!
            if (state != LoadState.OK)
            {
                string playerPrefKey  = "Save." + m_key + ".sav";
                string prefSaveString = PlayerPrefs.GetString(playerPrefKey);
                if (!string.IsNullOrEmpty(prefSaveString))
                {
                    try
                    {
                        using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(prefSaveString)))
                        {
                            state = LoadFromStream(ms);
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.LogWarning("Unable to parse save data in PlayerPrefs");
                        Debug.LogWarning(e);
                    }
                }
            }
            return(state);
        }