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.