An Easy Way to Break Your Save Data in XNA

broken-hard-disk
Late last week, I finally managed to pass an update for Robot Legions out onto the Xbox Live Marketplace. I wasn’t originally planning to release an update this early (I have some other features in-the-works for the “real” next update), but… I had a bug. A nasty save data killing bug.

Yuck.

So, in the interest of helping other XNA devs out there, let me tell you about what I screwed up and how I fixed it.

So, let’s get to the meat. Let me show you how I screwed up my save data handling.

First, this is how I read my save data:

// Deserialization
XmlSerializer s = new XmlSerializer(typeof(T));
if (container.FileExists(filename))
{
    TextReader r = new StreamReader(container.OpenFile(filename, FileMode.Open));
    saveDataItem = (T)s.Deserialize(r);
    r.Close();
}
container.Dispose();

And this is how I wrote it:

// Serialization
XmlSerializer s = new XmlSerializer(saveDataItem.GetType());
TextWriter w = new StreamWriter(container.OpenFile(filename, FileMode.OpenOrCreate));
s.Serialize(w, saveDataItem);
w.Close();

See the bug? Yeah, well, neither did I. You see, there was this little problem with how I wrote my save data. The code above would work in most cases, but if I wrote data that was shorter than the previous file’s save data, the old save data would only be partially overwritten. Any bytes of the old save data not explicitly overwritten by the new save data would remain appended to the end of the new save data. Thus potentially turning data like this:

<RobotSaveData><Cash>12345</Cash></RobotSaveData>

Into data like this:

<RobotSaveData><Cash>123</Cash></RobotSaveData>a>

And totally confounding the XML deserializer. Voila! Broken save data and unhappy customers. To top it off, as far as I can tell, this behavior doesn’t replicate on Windows, only on the Xbox 360. You can imagine my consternation.

Enough about what went wrong. Here’s how I went about fixing it. I changed the way I opened a file for writing my save data:

// Serialization
XmlSerializer s = new XmlSerializer(saveDataItem.GetType());
TextWriter w = new StreamWriter(
    container.FileExists(filename) ?
    container.OpenFile(filename, FileMode.Truncate) :
    container.OpenFile(filename, FileMode.Create)
);
s.Serialize(w, saveDataItem);
w.Close();

This big difference here is that I use FileMode.Truncate when opening the file for writing save data. By doing this, I’m able to dispose of the old contents and completely overwrite them with my new save data.

EDIT: I was recently informed by one of the fine members of the XBLIG community that simply using FileMode.Create when I open a file is sufficient for my purposes since it’s behavior when a file exists is the same as FileMode.Truncate.

Share this Article:
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • Print