Monday, June 14, 2010

One thing I like to do is play Rock Band. However, I often find that I don't remember what songs I just played in a playlist, and sometimes I want to check and see if I got a new high score. My first thought was to sniff the packets sent to the online leaderboards and pull the songs and the scores out of that data. Unfortunately, the packets are encrypted, so I gave up on that idea. But I had a new idea. I could point my computer's camera at the screen and record a video.

Making a video of the gameplay would eat up disk space and it would be a pain to go through a long video to see all the songs. But, going back to my first idea of sniffing packets, I could have some scheme of taking pictures and saving the ones around the time when packets were sent to the leaderboards. After finding out how to take pictures using the quicktime API and trying it out, I wrote some code that took a picture every 5 seconds, and interleaved the pictures with tcpdump output. I found that 32 byte packets were being sent every few seconds to Xbox Live, and 80 byte packets were being sent every once in a while, but larger packets were always sent after finishing a song. So then I wrote it to take a few pictures when seeing the larger packets. It worked really well. It takes some extraneous pictures due to other packets. Fortunately, I don't have lots of Xbox friends that would cause lots of notification packets.

Now I can play a dozen or so songs, and then go back and see how I did, instead of worrying about not remembering what I just played after playing only 3 or 4 songs.

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import quicktime.QTSession;
import quicktime.io.QTFile;
import quicktime.qd.Pict;
import quicktime.qd.QDConstants;
import quicktime.qd.QDGraphics;
import quicktime.qd.QDRect;
import quicktime.std.StdQTConstants4;
import quicktime.std.image.GraphicsExporter;
import quicktime.std.sg.SequenceGrabber;

public class Scorer {
private static ArrayBlockingQueue<Long> queue = new ArrayBlockingQueue<Long>(20);
private static boolean running = true;

private static void capturePhoto(String filename) throws Exception {
try {
QTSession.open();
QDRect qdRect = new QDRect(640, 480);
QDGraphics qdGraphics = new QDGraphics(qdRect);
SequenceGrabber sequenceGrabber = new SequenceGrabber();
sequenceGrabber.setGWorld(qdGraphics, null);
GraphicsExporter graphicsExporter = new GraphicsExporter(StdQTConstants4.kQTFileTypePNG);
graphicsExporter.setInputPicture(Pict.fromSequenceGrabber(sequenceGrabber, qdRect, 0, 0));
graphicsExporter.setOutputFile(new QTFile(filename));
graphicsExporter.doExport();
} finally {
QTSession.close();
}
}

private static void recorder(String dir) {
try {
PrintStream out = new PrintStream(new FileOutputStream(dir + "index.html"));
out.println("<html><body>");
int i = 0;
long t = 0L;
while (running) {
queue.take();
if (System.currentTimeMillis() - t < 30000L)
continue;
out.println("<br>Start:" + new Date() + "<br>");
for (int j = 0; j < 3; j++) {
capturePhoto(dir + i + ".png");
out.println("<img src=\"" + i + ".png\">");
i++;
TimeUnit.SECONDS.sleep(1);
}
out.println("<br>End:" + new Date() + "<br>");
queue.clear();
t = System.currentTimeMillis();
}
out.println("</body></html>");
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}

private static void sniffer() {
try {
Process process = new ProcessBuilder("/usr/bin/sudo", "/usr/sbin/tcpdump", "-n", "-l", "port", "3074").start();
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
while (running) {
String s = in.readLine();
if (s == null)
break;
if (s.endsWith("length 32") || s.endsWith("length 80"))
continue;
queue.offer(System.currentTimeMillis());
}
process.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) throws Exception {
new File("/tmp/score").mkdir();
new Thread() { public void run() { recorder("/tmp/score/"); } }.start();
new Thread() { public void run() { sniffer(); } }.start();
System.in.read();
running = false;
}
}

No comments:

Post a Comment