My game engine now runs on the Ouya (a new Android-based device using a Tegra 3 ARM processor!)
One of the big challenges that I encountered during the port was asset
loading. Android packages, which are delivered as .apk files,
typically load assets at run-time by reaching back into the .apk
file. The Android provided API for accomplishing asset loading in Java
is appropriate but the Android NDK C API has some shortcomings. Why
would I say that when the Java API and the C API are almost identical?
C applications that handle streams of data from files are typically
designed around the file pointer abstraction (FILE*
) and the Android
API isn’t compatible. This means, typically, that you would need to
alter the code of any third-party libraries that you are using (if
they deal with files) in order to port them the Android asset
API… until now!
My engine uses quite a few third-party libraries, specifically:
Box2D for physics
lobogg / tremor for sound and music
Lua for scripting
stb_image for image loading
All of these libraries, except Box2D, provide features that already exist behind some other Android API. The reason I explicitly provide these features again through my own libraries is for portability. I do my quick-turnaround build testing on my Mac but I want the same code to work exactly the same way on the Ouya. The simplest way to achieve this is to provide a lot of my own low-level support. The more “pure” way to achieve this would be to create an abstraction layer that used the Android APIs and the OSX APIs under the hood.
libogg, Lua, and stb_image all use the FILE*
API to load files from
disk. They call fopen
when they want to open a new file and fread
to extract data from it. They also occasionally use fseek
to jump
around.
One amazing and well-hidden fact about the FILE*
API is that it is
actually polymorphic and extensible! (A surprise from C, right?) We
can create a FILE*
that uses code we wrote to satisfy fopen, fread,
fseek, and fwrite
requests. We can use this little-known feature to
wrap the Android asset API in a FILE*
API instead.
Here’s how:
1 2 3 4 5 6 7 8 |
|
The funopen function creates a new file pointer that will delegate
control to the functions that you specify when the user uses fread and
friends on that file pointer. The first parameter to funopen is the
“cookie” to associate with the FILE*
. This is arbitrary data that will
be given to your delegate functions whenever they are called. OOP
programmers should think of it as the self (or this
) pointer.
Let’s write those delegate functions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Now we can use the FILE*
API to access our assets!
1 2 3 |
|
This is great but we still have to call android_fopen to create our
special FILE*
. If we stopped hacking here then we would still need to
open up all those third party libraries to change the fopen calls to
android fopen instead. We can do better.
If we’re willing to say that ALL file access should go through the asset API then we can achieve that quickly. We’ll just create a header for our new android fopen library and redefine fopen using a macro:
1 2 3 4 5 6 7 8 |
|
Now we just include this header in our code and we can use fopen
as
normal! But, again, we’re still adding code to those third party
libraries. Thankfully, gcc
has the answer.
1
|
|
The -include
argument causes gcc
to treat your code as-if you had
included the provided file at the very top of your code. Now, to port
Lua and libogg and any other big-fancy library that uses FILE*
to
Android, all we need to do is tweak the build system to include our
magic header.