Capturing screenshots from Xbox XNA games

Originally posted to Shawn Hargreaves Blog on MSDN, Monday, January 8, 2007

Taking screenshots of Windows games is easy thanks to that handy little PrintScreen key, but Xbox has no built in screenshot functionality so we have to roll our own as part of our game code.

XNA doesn't have any way of transferring files back from Xbox to the development PC, but it does provide the ability to print out information using the System.Diagnostics.Trace API. Printing out an entire screenshot in text format isn't exactly fast, and this solution is certainly a bit of a hack, but it works quite well in practice!

To print out a screenshot from an Xbox game, call this code from very end of your Draw method:

    int w = device.PresentationParameters.BackBufferWidth;
    int h = device.PresentationParameters.BackBufferHeight;

    using (Texture2D screenshot = new Texture2D(device, w, h, 1,
                                                ResourceUsage.ResolveTarget,
                                                SurfaceFormat.Color,
                                                ResourceManagementMode.Manual))
    {
        device.ResolveBackBuffer(screenshot);

        Color[] data = new Color[w * h];

        screenshot.GetData(data);

        Trace.WriteLine(string.Format("ss {0} {1}", w, h));

        const string prefix = "ss ";

        StringBuilder sb = new StringBuilder(prefix);

        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                sb.AppendFormat("{0:X8}", data[(h - y - 1) * w + x].PackedValue);

                if ((y * w + x & 63) == 63)
                {
                    Trace.WriteLine(sb.ToString());

                    sb.Length = prefix.Length;
                }
                else
                    sb.Append(' ');
            }
        }

        if (sb.Length > prefix.Length)
            Trace.WriteLine(sb.ToString());

        Trace.WriteLine("ss end");
    }

It would be a good idea to hook this up to a spare controller button so you can trigger a screenshot whenever you like while playing the game. Open up the Output window in C# Express, launch your game in the debugger by pressing F5, and watch the pretty numbers appear each time you take a screenshot.

Note how each line of screenshot data is prefixed with the characters "ss". This makes it possible to gracefully ignore any other unrelated printout, and also to properly handle multiple screenshots, so you can print out as many as you like in a single debug session.

When you are done, press ctrl+A to select the entire contents of the output window, then ctrl+C to copy it to the clipboard. C# Express will warn this is a lot of data, which is indeed true, but that's ok.

Now we need a Windows program to reassemble the printout into some more useful format. I wrote a little utility that converts text data from the clipboard into TGA files on disk. For some unknown reason the Clipboard.GetText method didn't work when I tried to compile this as a console application, so in the end I started with a default Windows Application project and added this code to my Form1 constructor:

    // Read screenshot data from the clipboard
    string clipText = Clipboard.GetText();

    string[] lines = clipText.Split(new char[] { '\r', '\n' },
                                    StringSplitOptions.RemoveEmptyEntries);

    int screenCount = 0;

    BinaryWriter output = null;

    const string prefix = "ss ";

    // Scan over the screenshot data
    foreach (string line in lines)
    {
        if (line.StartsWith(prefix))
        {
            string choppedLine = line.Substring(prefix.Length);

            if (choppedLine == "end")
            {
                // End of a screenshot
                output.Close();
                output = null;
            }
            else
            {
                string[] tokens = choppedLine.Split(new char[] { ' ', '\t' },
                                                    StringSplitOptions.RemoveEmptyEntries);

                if (output == null)
                {
                    // Start of a new screenshot
                    string filename = "screenshot" + screenCount + ".tga";

                    screenCount++;

                    output = new BinaryWriter(File.OpenWrite(filename));

                    // Write the TGA file header
                    output.Write((byte)0);                  // id length
                    output.Write((byte)0);                  // palette type
                    output.Write((byte)2);                  // image type
                    output.Write((short)0);                 // first color
                    output.Write((short)0);                 // color count
                    output.Write((byte)0);                  // palette entry size
                    output.Write((short)0);                 // left
                    output.Write((short)0);                 // top
                    output.Write(short.Parse(tokens[0]));   // width
                    output.Write(short.Parse(tokens[1]));   // height
                    output.Write((byte)32);                 // bits per pixel
                    output.Write((byte)8);                  // flags
                }
                else
                {
                    // Add data to an existing screenshot
                    foreach (string token in tokens)
                        output.Write(uint.Parse(token, NumberStyles.HexNumber));
                }
            }
        }
    }

Run that while the screenshot printout is in your clipboard, and it will save out a separate TGA file for each screenshot that you captured.

Have I mentioned how much I love C# yet, btw? It is every bit as nice as Python for string parsing, and very nearly as nice as Perl, but runs an order of magnitude faster than either. Sweet.

Blog index   -   Back to my homepage