In 1996, Tomb Raider 1 invented the category of third-person 3D action-adventure games. After nearly twentry years I wanted to play it again, but this time from the couch on TV via my OUYA android gaming console.
It turned out that the game was ported to Android in 2015 and is available in the Google Play store. I asked the Square Enix support if the game supports gamepads or joysticks, and they confirmed that it works at least with the Moga Hero Power and Moga Pro Power controllers. So basic support was there, and the rest would be button mappings - I thought :)
Downloading
The Google Play store is not available on the OUYA (only the native OUYA store is supported), so I bought the game for 0,99 US dollars on a normal Android phone. Installing it on the OUYA required me to get hold of the game's package file, and I asked the support for the official .apk download URL, telling them the account email I used to buy it. They declined my request:
Unfortunately for security reasons it is not possible for us to advise you how to download the game unless you are using the Play store. If you are unable to do this then it should be possible for you to speak to them regarding a refund.
We are sorry for any disappointment that this may have caused.
Kind regards,
The Square Enix Support Centre team
I tried an APK Extractor app on the phone, which gave me an .apk file of 4 MiB while the original download was 320 MiB.
Then I went the usual route: Use mitmproxy to get hold of the URLs that are called when the phone downloads and installs the game. I used Android-x86 in VirtualBox for this since it was easier than to install the mitmproxy certificate on a real phone.
It turned out to be two files that were downloaded:
- com.squareenix.tombraider1classic/40, an Android Expansion file (.obb), 316 MiB in size, MD5 hash 65f56cf31d5fc26e284ec26ef47636fa
- com.squareenix.tombraider1classic/46/apk_gz, the actual Android package file, 4.4 MiB in size, MD5 hash b4a232b4c333211115169671b9a51ad6
The Android package installer installed the .apk file normally, and saved the expansion file at /sdcard/Android/obb/com.squareenix.tombraider1classic/main.40.com.squareenix.tombraider1classic.obb. The Tomb Raider app would show an error message if that file did not exist.
Running
After installing .apk and .obb file I started the game. The copyright notice, then the logos of Square Enix, Core and Realtech-VR appear, and the first frame of the intro video. Then it crashed, and I was back at the launcher.
I sent the log I got via adb logcat to the support, and again got dismissed:
We are sorry to hear that you are having trouble launching Tomb Raider on your OUYA. Unfortunately we cannot provide support on this platform as the game is not distributed on or to the OUYA.
We apologise for any inconvenience this may cause.
Fixing the game
Hammering
A day later I found a thread on ouyaforum.com about Tomb Raider Classic, and it gave me an important hint: You can skip the intro movie by hammering the red button on the controller. If you're quick enough and press it within the 100ms window before the movie player crashes, you'll get into the main menu!
It was cool to see the main menu, and I could even start the training session in Lara's home, as well as the normal game - if I managed to skip their intro videos.
Then the screen was filled with the button overlays, and I could use the OUYA's controller touchpad to press them. Apart from the red O button on the controller, no other button, analog stick or the digital pad worked.
Decompiling
So close, but so far away - still the Square Enix support had said there was controller support, and I just had to find out how to make it detect the OUYA controller.
At first I used jadx-gui to decompile the .apk file into nearly-real-java files that I could inspect. The input handling code is at com.realtechvr.v3x.input, and there are three classes:
Gamtel was only activated if your device model was R800i, the input device model contained gametel or if the default device input method class starts with com.fructel.gametel.
The HoneyComb input handler looked more promising; it had a whitelist for controllers for Android versions < 4.3:
- NVIDIA Corporation NVIDIA Controller
- Broadcom Bluetooth HID
- WikiPad Controller
- Sony PLAYSTATION(R)3 Controller.
- EI-GP20
- Gamepad
At first I pondered how to change the input device name, but that required modifying and re-compiling the kernel, which I did not want to do again.
Then I saw that for Android 4.3 or later, every controller was accepted! The choices I had were:
- Upgrade the OUYA from 4.1 to 4.3 - which will never ever be supported now that the OUYA developers were bought by Razer ; I don't expect any updates to the platform anymore
- Make the OUYA return an API version of 18 instead of 16, which requires modifying the Android core and would break many other things.
- Modify the Tomb Raider source code.
For simplicity, I chose the last option :)
smali to the rescue
The java code produced by jadx cannot be compiled, so I had to resort to apktool which generates .smali files that contain a plain text version of the Android Dalvik VM byte code .
I quickly found the HoneyCombInputController::isSupported method and the API version comparison code:
.line 133 .local v2, "ids":[I sget v4, Landroid/os/Build$VERSION;->SDK_INT:I const/16 v5, 0x12 if-lt v4, v5, :cond_0 .line 135 invoke-static {}, Lcom/realtechvr/v3x/input/HoneyCombInputController;->initBindings()V
0x12 is the Android version being compared, and I simply changed it to 0x10 (16), which is the OUYA's android version.
I re-packaged the code up with apktool, signed it and installed it via adb on the OUYA.
Then I started it, skipped the intro, and ... it worked! Finally I was able to play the game as it was meant to be! I rejoyced like a child.
Another take on the movies
Now that I knew how to fix things, I checked the movies issue again. The code told me that even when I removed the movie files from the .obb package, the movie player was still initialized and started.
I hoped that preventing the player from being initialized and started also prevented the crash. There was an if statement in the SDLActivity::playFMV method which checked if the Android version was >= 11, and I simply reversed the condition in the corresponding .smali file:
.line 921 sget v5, Landroid/os/Build$VERSION;->SDK_INT:I - if-gt v5, v6, :cond_1 + if-lt v5, v6, :cond_1
After building, signing, zip-aligning and re-installing the game on the OUYA I could start it without any crash!
Menu entries
--- a/AndroidManifest.xml 2015-09-15 20:19:29.292409223 +0200 +++ b/AndroidManifest.xml 2015-09-16 07:29:40.083819273 +0200 @@ -17,6 +17,7 @@ <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> + <category android:name="tv.ouya.intent.category.GAME"/> </intent-filter> <intent-filter> <action android:name="android.intent.action.MAIN"/>
Screenshots
Now that all is done, I could enjoy some hours of discovering the mysteries of caves with Lara:
Open issues
During playing I noticed several small problems:
-
Sometimes when getting into the main menu, the buttons do not work. I had to press U twice to get to the OUYA game context menu and press A to get back to the game; the buttons then usually work.
During gameplay sometimes they stop working and it seems as if you're in look-around-mode. Pressing L2+L3+R2+R3 and the look-around analog stick (the right one) usually fix this.
- There is no "walk" button, but the left analog stick can be used for both walking and running.
- On few occasions, the game was twice as fast as normal, but changed back to normal speed after a couple of seconds. This seems to have to do with the location within the game; when you visit the same place again, the speed increases again.