Android development: Loading ETC1 textures from NDK

This post is probably irrelevant to anyone but Android NDK game developers, nevertheless I wanted to share my findings to help out those with the same problem. (Yes, this is not the Jewels post-mortem I promised, it is still coming :))

As you may know, ETC1 is the common texture compression technique on all Android devices supporting OpenGL ES 2.0. Android SDK comes with this aptly named tool etc1tool, that allows compressing PNG images to the ETC1 format. Android SDK has couple of APIs for loading these textures from the Java side (ETC1Util), but what about the NDK (i.e. the native side, with C or C++)? I was adding ETC1 texture support to my project and wanted to load them from native code.

The tool packages the compressed ETC1 data into a container format called PKM. The problem is that the specs of that format are nowhere to be found! At least, I did not find them despite spending quite a bit of time searching. The docs state that "The PKM file format is of a 16-byte header that describes the image bounds followed by the encoded ETC1 texture data", which is the best I could find.

My first guess was that there would be a tag/id, two 32-bit integers for the bounds and some padding in those 16 bytes. That was close, but not quite. I had to actually install a hex editor (haven't needed one in years) and take a look at the file to see what actually was going on!

Indeed the identifier "PKM 10" was there, but I was puzzled about the bounds. Only after I read somewhere that the image dimensions for ETC1 must be multiplies of 4, it started to make sense. The bounds were not in two 32-bit numbers but in four 16-bit shorts. The bounds appeared twice: first rounded up to the nearest multiple of 4, and then in the original dimensions.

UPDATE: There is actually a format specifier after the "PKM 10" string (not padding), which specifies the number of mip maps (always zero). It is important to note that all the values are saved in big endian encoding.

So here is the format I used, in pseudoish-code:

struct ETC1Header {
    char tag[6];        // "PKM 10"
    U16 format;         // Format == number of mips (== zero)
    U16 texWidth;       // Texture dimensions, multiple of 4 (big-endian)
    U16 texHeight;
    U16 origWidth;      // Original dimensions (big-endian)
    U16 origHeight;

That totals to 16 bytes as stated in the docs. Hopefully this helps loading those textures from native code! :)

Disclaimer: all this is based on my findings and the code does work for the textures I currently use, but I take no responsibility if the specs are wrong or bound to change.

The End Of The Road

…for Jewels, that is. Okay, that was overly dramatic, I'll be the first to admit it! :D

Anyway, all jesting aside, it is time to move on. What I'm announcing is that I will not be actively supporting Jewels (nor iJewels) from this point onwards. Not to say that I've been very active lately in any case, but better set it straight out.

Instead I will be focusing my development efforts (time limited for various reasons — not the least of which is the fact that I'm working at home with a little child roaming around.. :)) toward my next game. Not to worry though, I have a feeling that if you liked Jewels, you'll going to like the next game even more… ;) The project is advancing slowly but steadily; I've written most of the technical back-end code and can focus on building the actual game play on top of the thing. I do have to draw all the artwork as well, so it is not going to be finished any time soon — hopefully sooner than later, though!

And no reason to worry, my dear Android users, despite my venting earlier I will be releasing for Android as well, and making sure both versions (Android and iOS) are on even ground this time. To be honest I've been using an Android device as my daily driver for almost a year now, it's good to be back! :D

* * *

Speaking of Android: the first mobile version of Jewels was released in late 2009 for Android, so I've been supporting this one game for over two years now (you could count the original PC-version from 2007 as well, but I didn't work on that but a few months in total). One thing is certain: mobile devices sure have changed a lot since 2009, my ancient by today's standards HTC Hero running Android 1.5 was cutting edge tech back then!

If you think the Android-version of Jewels is really crappy compared to the iOS-version (released in late September of 2010), you are entirely correct — after all it was the first thing I wrote after I got my Hero, and it was hastily ported from the earlier XNA-test project I had done (a.k.a the Jewels PC-version).

It would be interesting to take a look at the horrible internals of the Android-Jewels and see what I would do (and will do, for the next game!) better this time around. So that is precisely what I shall do: when I get the chance I'll write a post-mortem about Jewels.

* * *

To close this post, let's have a look at a few statistics for Jewels:

That one is the for Android-version. 21 million total installs during these two+ years. Only 6.26 million currently active (i.e. on users devices), though. The largest active installs I've seen was between 8-9 million. ~150 thousand ratings with average rating of 4.4 stars. For the iOS-version (courtesy of AppFigures):

About 4.25 million downloads. I couldn't get the average rating across all countries, so the stats above reflect US only (easily the largest of my user bases). Still pretty good. :)

Looking at the volumes, I'd hazard a guess that Jewels is the second most downloaded mobile game from Finland, after that one franchise involving pissed-off birds… (Do correct me if I'm wrong!) Not too shabby for a crappy first attempt at doing a mobile game. ;)

See you for the Jewels post-mortem later, bye!

Jewels on Amazon Appstore!

Just a quick update: Jewels (the Android-version, naturally) is now finally available on the Amazon Appstore. Be sure to take a look: here. Now, let us drive it a bit further up in the top charts, shall we? :D Seriously though, it's there — grab the game if you want to. :)

The Sad State of the Android Market..

Warning: this post is mostly a rant, with a bit exaggerated (but not much!) title to boot..

I've been meaning to write this one for a couple of months now, but never really found the time and actually got around to do it. So here goes. I started noticing a lot of games on the Android Market with familiar looking icons, and after closer inspection, it's clear that most of the graphics have been stolen from Jewels. I have not downloaded the games, but I would hazard a guess that the sounds are also taken from Jewels for most of these clones.

Do a quick search for "Jewels" on the Android Market. What do you find? I counted 8 (that's right, eight!) games that have graphics stolen from my game. Six (6) of those eight games have one of my Jewels graphics as icon image. One clone is the worst offender. It has stolen the graphics, the icon (yes, the actual Jewels-icon a couple of versions back) and get this… even the freaking app description is copied from my game! The best part — and the gist of this entire posting — the game is named Jewels.

Now let's be realistic: "jewels" is a common word. (All those eight games have the word in their title; most probably there other clones with stolen assets with other names.) It's not like I invented the word. But why on Earth does Android Market allow two apps (two games, both in the Casual-category) to share the exactly same name? Why? Especially when the copycat app clearly has stolen almost everything from the original app. I mean what the hell? Why couldn't they check for the app name and prevent duplicate named apps from the Market? I can see them allowing same named apps for different categories, but I don't understand the current policy at all. (Perhaps that other Jewels has some whitespace characters in its name, I don't know, but frankly I see no reason why names like "Jewels_", where _ is a space, should be allowed.)

The Market is also riddled with app spammers, developers (in the lowest possible meaning of the word) that pump out dozens of sub-standard apps and spam them in with different developer accounts. This is of course a whole different issue, and one that doesn't really matter to me, but it lowers the quality of the marketplace on the whole. I feel that Google should do some cleaning up on the Market, get rid of all the crap — and of the spammed apps using stolen assets (the most obvious cases, at least).

All this "steal assets from a popular game, and name it similarly" is of course to get a share of the attention those popular games receive. I guess I should be flattered that people bother to steal those graphics? :P Nevertheless, I'm sure that more popular apps have it worse than I do: look at all those wanna-be Angry Birds -games on the Market. Just sad…

* * *

As you can see, I'm not thrilled with the Android Market as it is now, and I shall carefully consider whether to bring my future games to the platform at all. Apple has things far better on the App Store; the control is much tighter, but at least it keeps some of the crap out. And they don't allow two apps with the same name, something that became clear to me with iJewels. Not to mention I've come to like the iOS-platform much more than Android, but that's a different story..

This stealing assets thing is also one of the reasons why I still haven't (and never will, it seems) added the music from iJewels to the Android-Jewels. It would be stolen the instant I hit the big ol' Publish-button on the Market. :(

Happy Holidays!

(a bit late now, but anyway…)

Happy holidays folks! :) Here is an appropriate picture of our baby girl, Nella (click to enlarge):