I'd actually forgotten about this game completely until the latest +Humble Bundle Android release. I was browsing the new games using the app they provide when I saw it and thought it would be a great game for the kids. Unfortunately after installing it I found that it wouldn't work! A Google search revealed:
- http://support.humblebundle.com/customer/portal/articles/332858-humble-bundle-for-android-1-faq
- http://support.humblebundle.com/customer/portal/articles/363197-toki-tori-for-android-4-0-
So, what to do? Well I assumed that there must be some Android platform related change that was causing the issue. Maybe this could be patched or worked around? First thing then is to have a look the logcat output when the game crashed:
E/AndroidRuntime(24470): FATAL EXCEPTION: main
E/AndroidRuntime(24470): java.lang.NoClassDefFoundError: android/view/ViewRoot
E/AndroidRuntime(24470): at com.polarbit.fuse.MainTask.processTouchpadAsPointer(Native Method)
E/AndroidRuntime(24470): at com.polarbit.fuse.MainTask.access$200(MainTask.java:42)
E/AndroidRuntime(24470): at com.polarbit.fuse.MainTask$RenderSurface.surfaceCreated(MainTask.java:338)
A bit of searching revealed this:
It seems that the Toki Tori developers (or porters or whatever) might have been a bit naughty and referred to an internal framework class that changed between GB and ICS as per this quote from the ViewRoot 2.2 source:
The top of a view hierarchy, implementing the needed protocol between View
and the WindowManager. This is for the most part an internal implementation
detail of WindowManagerImpl
.
At this point I had two choices:
- try and reverse engineer the native method processTouchpadAsPointer() and attempt to patch it
- see what would happen if I simply didn't call it.
When reverse engineering Android code I usually just use the excellent command line utilities: APKTool and Dex2Jar and then run the resulting jar through JD-GUI to have a look.
However on this occasion I stumbled upon the most excellent Virtuous Ten Studio which automates most of the drudgery out of the process. I'd really recommend it and suggest you go and buy a licence to support the devs if you can afford it! So following the stack trace we get to the surfaceCreated method of the RenderSurface class which looks like this in smali:
.method public surfaceCreated(Landroid/view/SurfaceHolder;)V .locals 4 .parameter "holder" .prologue const/4 v3, 0x1 .line 332 iget-object v2, p0, Lcom/polarbit/fuse/MainTask$RenderSurface;->this$0:Lcom/polarbit/fuse/MainTask; iput-boolean v3, v2, Lcom/polarbit/fuse/MainTask;->mHasSurface:Z .line 334 iget-object v2, p0, Lcom/polarbit/fuse/MainTask$RenderSurface;->this$0:Lcom/polarbit/fuse/MainTask; iget-object v2, v2, Lcom/polarbit/fuse/MainTask;->mContext:Landroid/app/Activity; invoke-virtual {v2}, Landroid/app/Activity;->getWindow()Landroid/view/Window; move-result-object v2 invoke-virtual {v2}, Landroid/view/Window;->getDecorView()Landroid/view/View; move-result-object v2 invoke-virtual {v2}, Landroid/view/View;->getRootView()Landroid/view/View; move-result-object v0 .line 335 .local v0, root:Landroid/view/View; if-eqz v0, :cond_0 .line 336 invoke-virtual {v0}, Landroid/view/View;->getParent()Landroid/view/ViewParent; move-result-object v1 .line 337 .local v1, viewRoot:Landroid/view/ViewParent; if-eqz v1, :cond_0 .line 338 iget-object v2, p0, Lcom/polarbit/fuse/MainTask$RenderSurface;->this$0:Lcom/polarbit/fuse/MainTask; iget v2, v2, Lcom/polarbit/fuse/MainTask;->mInstance:I #calls: Lcom/polarbit/fuse/MainTask;->processTouchpadAsPointer(ILandroid/view/ViewParent;Z)Z invoke-static {v2, v1, v3}, Lcom/polarbit/fuse/MainTask;->access$200(ILandroid/view/ViewParent;Z)Z move-result v2 if-ne v2, v3, :cond_0 .line 350 .end local v1 #viewRoot:Landroid/view/ViewParent; :cond_0 return-void .end method
The interesting bit being this line of code:
if-eqz v0, :cond_0
Which is a basic null check before calling some additional code and finally the nasty method we want to get rid of. Hmmm looks like an interesting candidate and an elegant patch point. If we change the code to this:
if-nez v0, :cond_0
Then the condition is inverted and the relevant code skipped....
Making the change and using Virtuous Ten Studio to rebuild the APK was a 30 second operation, and guess what... it WORKS!
That is to say: I've played several levels and not only does the game now start, but it appears to work flawlessly.
Obviously I can't share the APK, but if the +Humble Bundle guys / gals want to get hold of me, I'd be happy to help them through this process. Alternatively, you could give it a go yourself.
Until next time, happy hacking!
-(e)