Firewatch is a story-centric game by Campo Santo and Panic. I played it all the way through this weekend and had a really great time with it. Besides the top-notch voice acting, gorgeous visuals, and mystery-filled plot, one feature that’s been getting a lot of attention is the disposable camera the player carries through most of the game. Starting with 18 unused shots on the roll, you can fill it up with snaps of trees and trails and plenty of sunsets peaking over the hills. At the end they’re all uploaded to a personal page on Firewatch.camera, where you can share them or order prints from the "Fotodome".

At the end of my own playthrough, I uploaded my shots and logged onto the site to see them.

But one of them came out… weird.

Strange distortion! And are those bits of QR codes in the corners? Could this be the beginnings of an ARG?!

Redditors sprang into action, loading the weird pic into Photoshop, filtering and cutting and turning it about in hopes of shaking out a secret.

My hunch was right—that was a QR code! Cleaned up and put back together, they found it led to…

Okay, maybe that's not exactly how it all happened.

Truth is, as I stared at the photo upload screen in a post-game slump, I got to wondering whether I could send in any old photo from my PC and have Panic print it off with the rest of the batch. I figured the game program probably sent the files to a hidden HTTP endpoint on firewatch.camera, so I broke out Charles Proxy, my tool of choice for HTTP interception. I like it because it’s easy to set up and supports SSL decryption, but other tools like mitmproxy could probably do the job as well. Looping back through the ending sequence again, I watched as my photos were retransmitted to The Cloud—this time with Charles listening in.

The -H flags let us set custom headers so our request looks like it’s coming from Unity, the engine the game was built in. The next line sends along an email address (to which Firewatch Camera will send our photos page) with the HTTP POST request. Further experimentation revealed that this field can be left blank.

As luck would have it, the server was happy to handle my request, responding back:

The game submits photos in reverse order, with an index parameter starting at 17 and counting down to 0 for a full batch. (It won’t take more than 18 photos total—I’ve tried.) The -F photo=@firewatch.jpg tells cURL to attach the contents of firewatch.jpg on my system as a file upload called "photo".

In short order I heard back from the server again:

{"status": "OK"}

Alright! One photo submitted. A couple more and it was time to try out the last endpoint on the list, roll/EvergreenBasinDrive/complete. Again matching the Charles log:

Some more playing around along these lines let me suss out the remaining requirements and error cases in the API. I bundled this knowledge into a plug-and-play Node.js library and command line tool as a little demo.

With that all done, we can get back to the original joke! Inspired by the internal name roll (short for "camera roll") used in the API, I:

grabbed my real Firewatch pics from their Firewatch.camera page,

made a Rick Roll QR code,

whipped up the fake, distorted shot in GIMP, splitting up the QR code and hiding it inside, and

used my new tool to upload it to a new Firewatch.camera page, alongside some of those photos I actually took in game.

All that remained was to stick the link on Reddit and let the fun unfold. 😉