Friday, 27 July 2012

BrogueX released

I've published my port of Brogue as an Android paid app.  It's basically playable with a little difficulty, and requires a little work to fix bugs, and a lot of work to enhance the Brogue libtcod-based UI for tablets and mobile devices.

All the details are on the link above.  Buying it supports the work required to make this a first class Android experience, and you get to direct the efforts involved should you choose.

Tuesday, 24 July 2012

Android development, Eclipse and SDL2

I've started playing with developing an Android application.  Here are my notes for future reference.

Debugging emulated C code in Eclipse

If I was developing in Java, Eclipse would be an easy choice.  When a breakpoint is hit or an exception happens within the Android emulator, execution will pause at the given point in whatever code is executing.  But only for Java.  If I step into C code, the debugger simply proceeds to run the program.  If the C code crashes, the debugger exits.  The only help is the LogCat window, and printf driven debugging.


No amount of web searching has located anyone else having this problem.

Eclipse's pre-compilation code checker gets in the way

Eclipse has this feature called Code Analysis.  It appears to be an editor level tool that shows you errors in your code before you actually compile.  The only problem is that it is unable to properly parse C code.  This means that not only do you get the wrong parts of preprocessor directive #if/#ifdef clauses being highlighted, you get officially lodged errors you have to fix before you are allowed to do things like debugging your code - even though it was just compiled without error!

Web searching reveals people who suggest clearing the analysis results seemingly every time you make a code change.  Others suggest rerunning the indexer.  Then there are suggestions to manually enter the mis-indexed symbols in a settings tab which does not exist for me.  And finally, some people hack the Eclipse project to specify the symbol, to get it to work.

I ended up disabling this feature via the Window->Preferences menu, within the C/C++->Code Analysis pane.  It sounds like it just isn't reliable enough to spend the time required to get it to work.


Android file access and SDL

An Android application is packaged in a zip archive, suffixed with ".apk".  One of the your directories, named "assets", is placed in this archive and can be accessed in a read-only fashion.  To access this, you need to make calls into the Java VM and your application object running there.  You grab an AssetManager, and from there can make calls on it to list whatever files and subdirectories are present within this directory, and of course can open and read from them.

Luckily, I am using SDL2 and can avoid this by just using the SDL_rwops abstraction layer.  This is not very well documented like much of the still in-development SDL2, but only provides access to the asset manager, and the assets directory within the apk file.  File system access I do not have much experience with, and still need to research.  But it looks like the standard C file-related functions are used.

Android asset access and libpng

I have a png image within my apk file, but libpng internally uses standard C file IO functions.  Working around this was a simple matter of taking advantage of the ability to specify read and write callbacks to libpng, which are used in place of the standard functions.
static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
 SDL_RWops *rwops = png_get_io_ptr(png_ptr);
 rwops->read(rwops,data,length,1);
}
static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
 SDL_RWops *rwops = png_get_io_ptr(png_ptr);
 rwops->write(rwops,data,length,1);
}
static void png_flush_data(png_structp png_ptr) {}

/* ... */

 SDL_RWops *rwops;
 if ((rwops = SDL_RWFromFile(filename, "rb")) == NULL)

 png_set_read_fn(png_ptr, (voidp)rwops, (png_rw_ptr)png_read_data);

There are some macros I should be using, which allow me to get rid of the rwops->opfunc(rwops pattern.  And what happens if I want to read or write png files from external storage, is another complication yet to be addressed.

Emulator logging, printf and Eclipse's LogCat tab

If I write to standard output from your application, it effectively goes nowhere by the looks of it.  So as advised by web searching, I use Android logging functions like __android_log_print whose output appears in the LogCat window in Eclipse  Better yet, I just bung these defines in a global include file -- which every project seems to have.

#ifdef __ANDROID__
#include <android/log.h>
#ifdef printf
#undef printf
#endif
#ifdef vprintf
#undef vprintf
#endif
#define printf(args...) __android_log_print(ANDROID_LOG_INFO, "libtcod", ## args)
#define vprintf(args...) __android_log_vprint(ANDROID_LOG_INFO, "libtcod", ## args)

#ifdef assert
#undef assert
#endif
#define assert(cond) if(!(cond)) __android_log_assert(#cond, "libtcod", "assertion failed: %s", #cond)
#endif
And I can just proceed to observe the existing printf calls in the LogCat tab in Eclipse.  I don't know why the Android assert function takes in the condition as a string for the first argument, as it doesn't appear in the Eclipse LogCat tab.

SDL2 also has logging functions which on Android, end up going through the Android logging API.  But as my application already has pre-existing printf calls, and I don't need the complication of an extended logging system, whatever.

Touch events and screen coordinates

Touch events have x and y coordinates.  But these are relative to the resolution of the touch device, and are not screen coordinates as events normally give.  It is an easy matter to fetch the device resolution and to scale it to screen coordinates.
 SDL_TouchFingerEvent *tfe=&ev->tfinger;
 SDL_Touch *touch=SDL_GetTouch(tfe->touchId);
 dx += (tfe->dx * windowWidth)/touch->xres;
 dy += (tfe->dy * windowHeight)/touch->yres;
 x = (tfe->x * windowWidth)/touch->xres;
 y = (tfe->y * windowHeight)/touch->yres;

Android "offline" SDK documentation

Being on Windows, I downloaded the SDK documentation through the SDK Manager program which comes as part of the SDK.  However, it has hard-coded references to Google javascript and font related web sites.  This means I get is a 10-20 sec loading delay every time I bring it up in a browser, or click a link within it to go to another page.

Android application's initial cwd

Porting an existing program to an Android application, being based on SDL it generally just works.  But what does cause crashes is the presumption that the current working directory is somewhere files can be stored.  What is the initial cwd for an Android application?

#include <errno.h> /* errno */
#include <unistd.h> /* getcwd */
#include <dirent.h> /* opendir / readdir */

/* ... */

 char cwdpath[100];
 DIR* pd;
 struct dirent *cur;
 if (getcwd(cwdpath, 99) == NULL) {
  printf("getcwd() failed and gave: %d\n", errno);
 } else {
  printf("getcwd() gave: %s\n", cwdpath);
  pd = opendir(cwdpath);
  if (pd == NULL) {
   printf("opendir() failed and gave: %d\n", errno);
  } else {
   while (cur = readdir(pd)) {
    printf("\"%s\"\n", cur->d_name);
   }
   closedir(pd);
  }
 }
Results in LogCat rows containing:
07-21 14:30:50.227: I/Brogue(923): getcwd() gave: /
07-21 14:30:50.227: I/Brogue(923): "."
07-21 14:30:50.227: I/Brogue(923): ".."
07-21 14:30:50.227: I/Brogue(923): "config"
This makes sense.  The install location of the apk file is not really relevant.  So the developer needs to query where to store these files, which may be either on internal or external storage.

SDL2 hard-coded namespace

Java code is referred to by namespace, and so JNI calls into C are made on the naming of that namespace.  This is a problem with SDL2 because the implementation of SDL2 relies on the use of the namespace "org.libsdl.app".  Anyone publishing their own app would want to do so in their own namespace, and if you change all the namespace references (whether in code, directory structure or configuration files), the JNI calls try and find the SDL C methods using your namespace.  This means that the internal SDL functions don't get called, and your app doesn't load due to the first UnresolvedLinkError.

This forum thread discusses it, and this blog post provides a file you can link against (with changes), in order to work around this problem. The changes required are replacing the use of this namespace with your own, and also verifying that all the SDL methods are wrapped.  I had to add several more.