Dirty Game Development Tricks

Ratchet and Clank: Up Your Arsenal was an online title that shipped without the ability to patch either code or data. Which was unfortunate.

The game downloads and displays an End User License Agreement each time it’s launched. This is an ascii string stored in a static buffer. This buffer is filled from the server without checking that the size is within the buffer’s capacity.

We exploited this fact to cause the EULA download to overflow the static buffer far enough to also overwrite a known global variable. This variable happened to be the function callback handler for a specific network packet. Once this handler was installed, we could send the network packet to cause a jump to the address in the overwritten global. The address was a pointer to some payload code that was stored earlier in the EULA data.

Valuable data existed between the real end of the EULA buffer and the overwritten global, so the first job of the payload code was to restore this trashed data. Once that was done things were back to normal and the actual patching work could be done.

via Gamasutra – Dirty Game Development Tricks.

Custom UINavigationController back buttons under iOS7

When you first create / get a reference to the navigation controller, set a global custom back image (replaces the chevron) with:

[self.navigationController.navigationBar setBackIndicatorImage:
    [UIImage imageNamed:@"CustomerBackImage"]];
[self.navigationController.navigationBar setBackIndicatorTransitionMaskImage:
    [UIImage imageNamed:@"CustomerBackImage"]];

The title of the back button is owned by the view that it points to, not the current view. You can make it blank by calling

self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]
    initWithTitle:@""
    style:UIBarButtonItemStylePlain
    target:nil
    action:nil];

in the view that the back button points to. So call it in prepareForSegue or before pushViewController:animated: or something. Setting a view controller’s title programmatically seems to reset it, so set it after setting the title.

You’ll see lots of advice to set self.navigationItem.leftBarButtonItems (which breaks swipe-to-go-back) then mess with interactivePopGestureRecognizer to fix swipe-to-go-back (which has some exciting doesn’t quite work right edge case problems). Don’t do that. Do this.

Programmatically adding a PagerTabStrip or PagerTitleStrip to a ViewPager on Android

The Lanyrd Android app does the majority of its layout programatically, because the views are described by a JSON file sent from the server. There aren’t a lot of resources on how to build a set of swipe-able views with a PagerTabStrip programmatically, so here’s what I’m doing:

pager = new ViewPager(context);

// ViewPagers must have a view ID. I use http://stackoverflow.com/a/15442997/656736 to generate them.
pager.setId(MyApplication.generateViewId());

PagerTabStrip strip = new PagerTabStrip(context);
ViewPager.LayoutParams layoutParams = new ViewPager.LayoutParams();
layoutParams.height = ViewPager.LayoutParams.WRAP_CONTENT;
layoutParams.width = ViewPager.LayoutParams.MATCH_PARENT;
layoutParams.gravity = Gravity.TOP;
pager.addView(strip, layoutParams);

Syncing between large S3 buckets

My problem is as follows. I want to copy many many files from one S3 bucket to another. So many, in fact, that s3cmd explodes with Killed because the oom killer has taken exception to it listing every single file in both buckets before starting (yes, I am using the latest version).

This script assumes that the copying account can read from the source bucket. If you want to copy files from a bucket owned by a different user, give the user running the script access to the bucket by visiting the S3 console, and adding the username of the copying user (email address works fine) as a “Grantee” in the Permissions for the source bucket, with “list” permisssions.

It’s a very stupid script. It’ll enumerate every file in the source bucket, then copy it to the target bucket blindly unless there’s already a file in the target with the same name. It doesn’t do checksumming, size comparison, or anything clever. It won’t delete files in the target that don’t exist in the source. It won’t update changed files. But it’s faster than s3cmd, it starts doing things instantly, it’s pretty cheap (and safe) to kill and restart, and it’ll run on a tiny EC2 instance, so you won’t incurr bandwidth transfer charges (you’ll still pay for requests. This isn’t free).

ASSETS_AWS_ACCESS_KEY_ID = '...'
ASSETS_AWS_SECRET_ACCESS_KEY = '...'
BUCKET_FROM = "source-bucket-name"
BUCKET_TO = "target-bucket-name"

import boto # developed on boto 2.9.6.
from boto.s3.connection import S3Connection
conn = S3Connection(ASSETS_AWS_ACCESS_KEY_ID, ASSETS_AWS_SECRET_ACCESS_KEY)
source = conn.get_bucket(BUCKET_FROM)
target = conn.get_bucket(BUCKET_TO)

# .list() is a magical iterator object, it'll make
# more requests of S3 as needed
for idx, entry in enumerate(source.list()):
    if entry.name.endswith("/"):
        continue
    print idx, entry.name
    if not target.get_key(entry.name):
        # this is a trade-off. Checking for target existence makes the first
        # run slower, but subsequent runs much faster, assuming only a subset
        # of files change.
        print "..copying"
        try:
            entry.copy(dst_bucket=target, dst_key=entry.name, validate_dst_bucket=False)
        except boto.exception.S3ResponseError, e:
            # Only copying files I have access to. Never bomb out half-way.
            print e

print "all done!"

(Side note. Be nice to future maintainers – don’t use the S3 Virtual Hosting stuff. You can’t rename buckets, and you can’t re-parent them so you’re stuck using that bucket forever. Put the files in a bucket with any old name, then use Cloudfront to serve them.)

renewing ACAccount credentials on iOS

An iOS 5/6 device can have system-level Twitter/Facebook accounts that become disassociated from the underlying service accounts. This means that the ACAccountStore framework will return you ACAccount objects that can be used to sign requests and get access tokens, but NONE OF THE REQUESTS WILL WORK and you’ll get MYSTERIOUS OAUTH ERRORS and LOTS OF SUPPORT MAIL and be GRUMPY because it WORKS FOR YOU JUST FINE.

I’m not certain how to fake this state. Changing your Facebook password and choosing to log out other devices is pretty reliable, albeit really annoying if you actually use Facebook for anything. Likewise, changing your Twitter password might work. I think restoring the phone from backup will also cause this problem, especially if it’s a iTunes-based non-encrypted backup, because the passwords aren’t saved, but the phone won’t prompt you at any point to enter them again.

When this happens to Twitter account objects, I see server responses to otherwise perfectly reasonably signed requests with the error Bad Authentication data and the error code 215. I used to get This method requires authentication when calling the old 1.0 API, because the 1.0 API interprets a bad signature as no signature (REALLY ANNOYING), but the 1.1 API is a lot stricter.

When this happens with Facebook, I see Error validating access token: The session has been invalidated because the user has changed the password and sometimes Error validating access token: Session does not match current stored session. This may be because the user changed the password since the time the session was created or Facebook has changed the session for security reasons.

The solution to this is the method on ACAccountStore:

- (void)renewCredentialsForAccount:(ACAccount*)account
                        completion:(ACAccountStoreCredentialRenewalHandler)completionHandler;

From the docs:

For Twitter and Sina Weibo accounts, this method will prompt the user to go to Settings to re-enter their password.

For Facebook accounts, if the access token has become invalid due to a regular expiration, this method will obtain a new one.

If the user has deauthorized your app, this renewal request will return ACAccountCredentialRenewResultRejected.

Calling this method on the ACAccount object you’re trying to deal with will make-or-break it. I find that the token either works afterwards, or doesn’t, but now it doesn’t work because the iOS device knows that it doesn’t have a valid token. You may still have to punt the user into the Settings app to fix it. For instance, in the case of Twitter I find that calling renewCredentialsForAccount will pop up a system dialog about the problem.

Facebook is more subtle – the ACAccount object can still be used to get an access token, but that token will fail on the server with The session has been invalidated because the user has changed the password still. However, punting them manually into Settings.app at this point will ask them for a Facebook password and things will work afterwards.

Alas, it’s asynchronous. You can work around that. Call it on the ACAccount instance before you try to do something serious with it, or do work in the callback.

I hope this helps -someone-, because it sure would have helped me 6 months ago.

Revolutionary Input Validation

From the amusing PHP Manual Masterpieces, we have a function that combines my two favourite pastimes – date operations and using PHP!

int frenchtojd ( int $month , int $day , int $year )

Converts a date from the French Republican Calendar to a Julian Day Count. These routines only convert dates in years 1 through 14 (Gregorian dates 22 September 1792 through 22 September 1806). This more than covers the period when the calendar was in use.

I like the entry on frenchtojd particularly because of the virtually glossed-over:

Notice, however, that the documentation does not say what happens if you pass in invalid data [..] I’d try to find out empirically, but my online PHP shell has actually disabled this function for vague “security reasons.”

He’s ranting about the core library, but practically takes it as read that random subsets of PHP may or may not be available on whatever hosting provider you happen to be using? Is this problem just minor background noise compared to everything else?

(Has anyone tried to make a sane (and necessarily slightly incompatible) fork of PHP that actually has a “parser” and a “grammar” and other incredibly advanced compsci concepts? Something that would let people write a subset of the language that would actually be understandable?)

On the bright side, maybe the rise of nginx will lead to the downfall of PHP. Right now it occupies a privileged position as the unofficial Apache scripting language, but using nginx you can only call PHP through fastcgi, where it has the same overhead as everything else, and has to compete on features.

Automatically activating a Python virtualenv

I have a lot of different python projects in a lot of different directories, and like them all to have their own virtualenv. Because I can’t even be bothered to type a single line of code to activate them, I’ve ended up with this (slightly insane) setup:

Every project folder has a ./venv/ which is where I keep the virtualenv. Then, in my .bash_profile, I have the following snippet:

__activate_venv() {
  if [ -f ./venv/bin/activate ]
  then
    . ./venv/bin/activate
    hash -r
  else
    if (type deactivate >/dev/null 2>&1)
    then
      deactivate
      hash -r
    fi
  fi
}
export PROMPT_COMMAND="__activate_venv"

In short – if the current directory has a venv/bin/activate script, then run it. Otherwise, if there’s something called deactivate that I can call, then do so.

So whenever I cd into a folder that has a ./venv/, it activates, and whenever I leave, it deactivates. This is probably insane.