date: 2014-10-12 author: Keith F. Prussing abstract: > post: true —

Salvaging Game Data on Android

A few weeks ago, the wife’s Samsung Galaxy S2 took the final plunge and bit the dust (mostly). It wound up caught in a cycle of powering on and off. Luckily, I was able to fiddle with it enough to grab the highscores.lua and settings.lua files from her Angry Birds games using a little script I wrote some time back.

$ cat pullab
#!/bin/bash
abs=$( adb shell pm list packages | \
        awk '{RS="\r"} /rovio/ {sub(/package:/,""); print $1}' \
    )
for app in ${abs}; do
    if [ $(echo ${app} | grep space) ]; then
    files="settings.lua highscores.lua eaglepurchases.lua \
        gamepurchases.lua episodepurchases.lua"
    else
        files="settings.lua highscores.lua"
    fi
    if [ -d ${app} ]; then
        rm -rf ${app}
    fi
    mkdir ${app}
    for f in ${files}; do
        adb pull data/data/${app}/files/$f ${app}
    done
done

This works marvelously well if all you need is the basic files. In theory, one can reverse this procedure by simply issuing the adb push command in the final loop. Unfortunately, this no longer works with Android 4.0 and newer (Ice Cream Sandwich and up) such as what is on the Galaxy S5.

So, what do I do? The only thing any rational person would do: Google it. One of my top hits was at Angry Birds Nest. It turns out that I should have used the baked in backup command and not simply pulled the files. Oh, well. After a few hours of poking and prodding, I was able to get my wife’s games completely restored. Keep in mind that what follows worked for me, and I do not claim to be an expert. I take no responsibility for anything that happens to any device other than my own for following these steps. Follow these steps at your own risk.

One of the tools mentioned on the Angry Birds Nest guide is the Android Backup Extractor. A bit of web searching turns up the development page. Reading through the documentation, I learned that the output of the adb backup command is simply an extended tape archive and abe.jar handles the interconversion between the formats!

A word of warning: abe.jar requires a Java Runtime Environment of 7 which apparently was not already installed on my MacBook Pro. Hunting down that fact took a couple of hours.

Now, to get things rolling, download the desired versions of Angry Birds and complete one level. This will ensure that the file structure for the games are in place. Now make a backup of the games and convert to a tape archive.

$ adb backup -apk -f angrybirds.orig.ab $(ls -d com.rovio.angry*)
$ java -jar abe.jar unpack angrybirds.orig.ab angrybirds.orig.tar
$ tar -xf angrybirds.orig.tar
$ tar -tf angrybirds.orig.tar > angrybirds.list

The first step above creates a backup of the games we want and includes the application file. Supposedly, the application file is not necessary, but I couldn’t get the restore to work properly without it. The subshell launched simply creates a list of all Rovio games I extracted files for with the script shown above. The second and third steps convert and extract the archive. The fourth step is necessary for repacking the data after we have updated the files. Apparently, the order of the files in the archive is critical for performing the restore.

Now I was able to explicitly update the files and generate a new tape archive which can be converted to an Android backup.

$ for f in $(ls -d com.rovio.angry*); do
> cp -i $f/*.lua apps/$f/f
> done
$ star -c -f angrybirds.tar -no-dirslash list=angrybirds.list
$ java -jar abe.jar pack angrybirds.tar angrybirds.ab
$ adb restore angrybirds.ab.

And my wife was able to get back to popping pigs! Now, a few notes: First, when I was originally trying to create the tape archive, I tried using tar, but that does not preserve the order of the files. I attempted to use pax, but I found files with long names. That meant I had to use star instead. Second, I was able to get all of this to work by getting the application file even though it is supposedly not necessary. Finally, in the future just use adb backup. The advantage of all of this was I got to learn more about the Android backup files and discovered two new command line tools that hadn’t crossed my radar yet: pax and star.


EDIT 2014-11-22: While in the process of cleaning up my computer to do a clean install of Yosemite, I came across one other script I used to pull data off of my old Android phones. As I already discussed above, this really a pointless script now. However, I don’t want to just throw it out and lose my notes. I also don’t want to create a repository just for it, and I don’t want it in my dotfiles. So, I’ll just put it here.

$ cat phonebu
#!/bin/bash

# We will also need to pull the apks to finish backing up the apps.
# This can be accomplished by using get_apk and simply pulling the
# apk. The ones to look out for are the ones on the sdcard (under
# /mnt/asec) as these will be pulled simply to a pkg.apk. We will
# need to make sure we know which one we are pulling using
#
#   adb pull ${apk}
#
# We could simply use sed to remove the -#/pkg portion of the apk in
# the data from packages (downloaded_apps). Actually, looking at the
# results, we may only need to remove the '/pkg' portion as the
# other apks appear to also contain the -# tag.
#
# We then need to look into what other bits of information we need
# to pull from the phone.

# Create the base directory to store the data
basedir=backup_$(date +%F)
mkdir -p ${basedir}

# Make the directory for the apk
mkdir -p ${basedir}/apks

# Find the downloaded applications excluding the Google ones.
apps=$( \
        adb shell pm list packages -f | \
        awk '{RS="\r";}  \
        (/data/ || /asec/) && !/\.android\./ \
        {sub(/package:/, ""); print $1}'  \
    )

for app in ${apps}; do
    # Get the install and the folder
    apk=$(echo ${app} | awk -F= '{print $1}')
    fld=$(echo ${app} | awk -F= '{print $2}')

    # Make the data folder
    mkdir -p ${basedir}/${fld}

    # Copy the application data
    adb pull /data/data/${fld} ${basedir}/${fld}

    # Determine the local name for the apk.
    local_apk=$( \
            echo ${apk} | sed -e 's|\/pkg||' \
            -e 's|\/[a-zA-Z]*\/[a-zA-Z]*\/||' \
        )

    # Pull the apk
    adb pull ${apk} ${basedir}/apks/${local_apk}