Detecting a corrupted file when loading data using binaryformatter / deserialize

Viewed 547

I'm working on a videogame where I save/load player savegames using c#'s binaryformatter. This works 99% of the time, but sometimes a user's savegame will get corrupted somehow, and then the game won't be able to read the file. If I could detect when the game encounters this problem, though, I could tell it to load a backup copy of the last good savegame, though, which would be helpful for everyone.

This is how I'm loading the data:

       if (File.Exists(Application.persistentDataPath + "/" + saveLoad.saveFileName))
       {

           BinaryFormatter bf = new BinaryFormatter();
           FileStream file = File.Open(Application.persistentDataPath + "/" + saveLoad.saveFileName, FileMode.Open);
           saveLoad.savedGames = (List<savedGame_latest>)bf.Deserialize(file);
           file.Close();
           success = true;

       }

By the way, this is the error when the game loads a corrupted file:

EndOfStreamException: Failed to read past end of stream

Any ideas? What I want is basically a way for the system to detect "oops no, that's corrupted" and to then be shunted to try and load the last safe backup instead.

2 Answers

well you have to open the file to check whether it is opening or not. what you can do is make a function which check whether the file can be opened or not -

To check for corrupted file

protected virtual bool IsFileCorrupted(FileInfo file)
{
    FileStream stream = null;

    try
    {
        stream = File.Open(FileMode.Open, FileAccess.Read, FileShare.None);
    }
    catch (IOException)
    {
       // File is corrupted
        return true;
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }

    //file is not corrupted
    return false;
}

Since BinaryFormatter stops reading the stream when it finishes you can simply add some hash or checksum value after the saved content without breaking the functionality.

Catching just EndOfStreamException detects only one possible corruption anyway.

Saving (hashAlg can be any HashAlgorithm implementation):

new BinaryFormatter().Serialize(stream, savegame); // regular data
var hash = hashAlg.ComputeHash(stream.ToArray());
stream.Write(hash, 0, hash.Length); // hash

And loading:

int hashLength = hashAlg.HashSize / 8; // get hash size in bytes
var bytes = stream.ToArray();
hash = hashAlg.ComputeHash(bytes, 0, (int)stream.Length - hashLength);
if (!hash.SequenceEqual(bytes.Skip(bytes.Length - hashLength)))
    throw new ArgumentException("Savegame Corrupted"); // gotcha!
savegame = (SaveGame)new BinaryFormatter().Deserialize(stream);
return savegame;

Try also online.

Related