Android Port
Current compiling instructions are here: Compiling for Android
FreeBASIC Android Port TODO
The FB port is already nearly good enough for the OHR, but further work is needed before merging upstream. There is lots of overlap between the Android port of FB and the OSX port. So I will be updating the OSX port to FB 0.90 (-gen gcc only) at the same time (there's not much to do).
libfb
- Run the testcases!
- Fix the console control character garbage printed by commandline programs. I wrapped all the ncurses stuff in #ifdef HOST_ANDROID wrappers (I should add a dedicated DISABLE_CURSES macro instead), but there's still lots of other stuff that directly writes control characters. These probably don't work because ncurses wasn't run to put the terminal in the right mode. Or maybe 'adb shell' is to blame.
- There's something weird going on with __fb_con.inited: different parts of FB seem to have different ideas about what INIT_CONSOLE means
- Clean up the build system for android/arm target
- Try to provide better replacements for the couple string conversion functions missing from bionic libc
- Investigate whether the constructor priority in fbrt0.c can be made to work. Priorities seem to be ignored when linking libfb.a into the .so, but work for command line programs.
- Find out why the rtlib destructor code isn't run for a commandline program if a destructor priority is specified in fbrt0.c
- Try to provide implementations for the rtlib/android stub functions
- Parts of the NDK docs about differences vs. Linux, and check whether non-stable ABIs are used
Compiler
- Add real __FB_ANDROID__ define (and ones for ARM etc too). Also plan to stop defining __FB_LINUX__ under Android, which could break code where __FB_LINUX__ is incorrectly being used instead of __UNIX__ (using __FB_UNIX__ is also incorrect since it's completely nonportable between FB versions; FB 0.90 changes it again!)
- Add an -entry command line argument to fbc (needed to specify SDL_main instead of main)
- Proper crosscompiling. Instead of only having Android-targetting build, make use of existing fbc commandline args to select an Android/ARM target
- Proper selection of ARM target, with -target and -arch. There's some discussion about -arch on the FB forum and in the FB git commit messages
- Support all of armeabi, armeabi-v7a, x86, mips
SDL/gfx_sdl for Android TODO
- Better support for different screen resolutions. Currently always run at 320x200 (1x zoom)
- Can choose zoom to fill as much of the screen as possible, and centre, but would still leave a lot of black on a lot of phones. Should we scale up the screen with interpolation to fit? If at all possible we should do zooming using SDL's OpenGL-based (I think) scaling rather than our own blit.c routines.
- zoom-and-center and zoom-and-stretch are the obvious choices, and it might even make sense to make the existing backends respect those for fullscreen
- We seem to get the actual screen size as long as keepAspectRatio is false. Note that SDL's virtual screen is always whatever size you request in SDL_SetVideoMode. However using a virtual screen a different width than the real screen caused weird behaviour for me...
- In what situations does android send a RESIZE event (which is passed from Java to the SDL android driver)? Might we care?
- Can choose zoom to fill as much of the screen as possible, and centre, but would still leave a lot of black on a lot of phones. Should we scale up the screen with interpolation to fit? If at all possible we should do zooming using SDL's OpenGL-based (I think) scaling rather than our own blit.c routines.
- Clean up all the startup SDL configuration stuff. Ideally we want to be able to totally get rid of this by handling everything ourselves. For now it's quite useful
- In emulator with GPU acceleration on, there's garbage on screen before first frame is drawn
- Change the mouse/keyboard mapping (probably just in AndroidAppSettings.cfg). Currently this SDL port lets you map physical buttons and the 4 onscreen buttons to emulated key presses at compile time
- Invoking the onscreen keyboard (should we use the native, or create our own? Ideally would never be used in most games)
- Link the Menu button to something useful... maybe not always ESC
- The SDL virtual joystick (bottom left) ought to have a dead zone in the centre
Engine TODO
- GOSUB/RETRACE is broken, but we use them so rarely in game that it doesn't make very much unplayable. Test possible fixes using the hero order/team menu in svn revision 5847
- Investigate reloadtest failures... or was it vectortest failures?
- Investigate slow downs. On my emulator and especially my phone, there seem to be long pauses between the end of .rpg upgrade and the titlescreen. Quitting is slow both on phone and desktop.
- Note that the armeabi platform doesn't have an FPU, floating point is emulated which is very slow (see below)
- Heavily scripted games are unplayable on a low end phone, probably ones like Bell of Chaos are unplayable on any phone. Will need a new script interpreter for these games
- Packaging games
- Figure out a better way to embed an RPG file other than sdl-android's download-on-first-run mechanism.
- Figure out how to change the package name, for example, com.ohrrpgce.gamename or com.authorsdomain.gamename
- Clean up our build system. I would like to let SCons handle creation of libapplication.so without using an Android.mk file.
- Support for other android architectures: armeabi, armeabi-v7a, x86, mips
- Have multiple .apks, or place all architectures in the same .apk? We don't need to provide armeabi-v7a (which has an FPU) if armeabi is fast enough for everyone
- See http://stackoverflow.com/questions/7080525/why-use-armeabi-v7a-code-over-armeabi-code for some info about this. There is a way to make the FPU work on armeabi for hardware that supports it (a compiler flag), although that still isn't as fast as armeabi-v7a
- The only routines we have which is explicitly floating point heavy (declares float variables) is frame_dissolved, ellipse and createminimap, plus anything that uses lots of random numbers (probably no other routines aside from those). frame_dissolved is really slow even on desktop. We could isolate those into a separate .so file to be compiled for either armeabi or armeabi-v7a (and even armeabi+VFP) while providing only armeabi build for the rest of the engine
- Figure out how to switch between release and debug builds easily. Currently there are build options spread all over the place
- Support for other android architectures: armeabi, armeabi-v7a, x86, mips
- Plan for improved mouse support and Plan for improved joystick support
- However, the mouse isn't suitable for selecting menu items, because they are too small to hit with a finger. Instead, we could use regular clicking for clicking on tiles and battle targets, and a joystick mode (swiping) to move the cursor in menus. The SDL port already has this implemented, at the bottom left corner (I'm not sure whether it emulates a joystick or a mouse)
- We could also make use of gyroscopes and accelerometers
- Currently, real gamepads are mapped to keyboard keys using SDL_ANDROID_set_java_gamepad_keymap() but we need a way to distinguish multiple gamepads.
- Android-specific directories/file placement, sdcard awareness
- Invisible on-screen buttons, including game-specific settings (eg. z,x,c).
- Can already be toggled using wrapper functions in gfx_sdl.bas.
- Suspending, resuming and quitting correctly the app (suspend and resume seem to work, but I don't know if the game pauses)
- Currently the Home button acts normally, but Back and Menu buttons are mapped to keys. The only way to quit is to go back to titlescreen, which can be quite hard!
- Suspending/resuming works fine for me (James)
- Perhaps a welcome screen showing recently played .rpgs instead of dumping users in the hard to use file browser
- Timidity for MIDI support (should be easy)
- The instrument patches suggested by SDL_mixer are 18MB. Can we find a smaller set?
- When packaging for a single game, we could conceivably scan all MIDIs in the game for actually used instruments, and only include those. Probably only a fraction would be used.
- Could keep MIDI as an optional download, hopefully shareable between the generic game player and all packaged games
- Why is gfx_sdl printed as "gfx_sd"?
Notes
- Don't try to access files or do other complicated stuff in module level code (other than game.bas and custom.bas), it can cause crashes! (Not sure why, possibly only if an error occurs)
- libapplication.so contains the actual native code for the application. It isn't loaded until after the app starts up, which happens in a separate thread. The SDL startup configuration menu runs and is usable before libapplication.so is loaded!
- libsdl.so is actually loaded earlier though
- Use <NDK>/toolchains/arm-linux-androideabi-4.7/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objdump -x and look for NEEDED lines to determine which dynamic libraries a binary requires.
- sdl-android/checkMissing.sh checks for missing symbols required by libapplication.so. Currently shows only dlopen, dlclose, dlsym.
Debugging
debug output is currently also mirrored to the adb logcat log. However calling debug before location of g_debug.txt is set is a bad idea, prefer calling external_log directly.
To debug, after building the .apk, cd to sdl-android/project/, push .apk to device and invoke ndk-gdb
adb install -r bin/MainActivity-debug.apk ndk-gdb --start --force --verbose
The program will run for a couple seconds before gdb attachs and it halts. Point gdb at the original FB sources, break on fb_End to catch runtime-detected errors and continue execution:
directory path/to/ohr/wip b fb_End c
You can interrupt execution by pressing Ctrl+C in gdb. When you do so you'll likely be looking at the wrong thread. "info threads" to look at all threads, "thread #" to switch to a thread, and "thread apply all bt" to print backtraces for all threads to help find the correct one.
Stripped libraries, including libapplication.so live in sdl-android/project/libs/armeabi/. Much more useful are the non-stripped originals, in sdl-android/project/obj/local/armeabi/
Splash Screen/Start-up
Image
The sdl splash screen can be replaced by replacing project/res/drawable/publisherlogo.png
We could probably make it configurable per-project by mimicing the symlink scheme of 'project/res/drawable/icon.png
First-time config
First-time config of screen size, gyroscope, etc, can be suppressed in AndroidAppSettings.cfg by editing FirstStartMenuOptions and setting it to a single space
FirstStartMenuOptions=' '
The default value of
FirstStartMenuOptions=
is actually translated into:
FirstStartMenuOptions='new Settings.ShowReadme(), (AppUsesMouse \&\& \! ForceRelativeMouseMode ? new Settings.DisplaySizeConfig(true) : new Settings.DummyMenu()), new Settings.OptionalDownloadConfig(true), new Settings.GyroscopeCalibration()'
Disabling the first-start java code works, but on the first load, you still see the splash screen for an instant, then it vanishes and you see the home screen for an instant, then the splash screen re-appears. This does not happen on subsequent starts
Config button at startup
The startup config button can (and probably should) be suppressed. I think leaving it enabled is only good for debugging, not for production. It can be suppressed by editing AndroidAppSettings.cfg
StartupMenuButtonTimeout=0
On-screen control images
The on-screen dpad and buttons look like sun-flares. The source-art for these are located in project/themes/Sun/
The easiest way to change these images is to edit them in-place, although there is not much obvious rhyme or reason to their naming scheme.
After you have edited them, you must run
cd project/themes/converter ./convert.sh
This might not work unless you first recompile the project/themes/converter/converter binary. You can do this by:
cd project/themes/converter rm converter make
the convert.sh script uses the converter binary to convert the theme images into files in project/res/raw which get included in the apk when you run build.sh
There are also copies of these images located in project/res/drawable and project/bin/res/drawable and it appears that at build-time the project/res/drawable/* files are copied to the project/bin/res/drawable location. These images get included in the apk, but as far as I can tell, they never actually get used.
From reading the code (much of which I do not understand!) I suspect that a previous developer was in the process of switching from one method to the other, and either did not finish the transition, or did not remove the old method.
How RedefineKeysScreenKb Works
Both virtual gamepad and actual gamepad buttons are mapped using RedefineKeysScreenKb in AndroidAppSettings.cfg
changeAppSettings.sh converts these into compile-time defines in the form SDL_ANDROID_SCREENKB_KEYCODE_# where the # is the index of the key 0 to 5. The default for these defines are set in project/jni/sdl-1.2/src/video/android/SDL_androidinput.h
These defines are then used in the SDL_android_init_keymap() function in project/jni/sdl-1.2/src/video/android/keymap.c to set up the keycode[] array
the SDL_android_init_keymap() function is wrapped for JNI in project/jni/sdl-1.2/src/video/androidSDL_androidinput.c and called by project/java/Settings.java using the name nativeInitKeymap
What I think we want to do is to use RedefineKeysScreenKb only to set up reasonable default keys for all ohrrpgce games, and then to have the game player manipulate SDL's keycode array to provide default button mappings for OUYA controllers (which should, in theory, be sane for most other controllers connected an android device) We can also use the same run-time button remapping method to implement the game-specific button-remapping described below.
Android metadata in rpg file
Some android-specific data about button-mapping and virtual gamepad display should be stored in the RPG file. This has already been mostly implemented. See: general.reld
Also relevant: Plan for platform-specific button names
Data Files
sdl-android has a strange mechanism for including data files. They are treated as downloads. You can put zip files in project/jni/application/ohrrpgce/AndroidData/ and then instruct the application to unzip them to /sdcard/Android/data/com.hamsterrepublic.ohrrpgce.game/files/ using the following command in AndroidAppSettings.cfg
AppDataDownloadUrl="!Dont Eat Soap Game Data|eatsoap.zip"
In the above example, the eatsoap.zip file contains eatsoap.rpg and ohrrpgce_arguments.txt containing
eatsoap.rpg
This scheme works, but the problem with it is that if you update and re-install the apk, the data file will NOT be re-unzipped.
Apparently the workaround is:
DeleteFilesOnUpgrade="libsdl-DownloadFinished-0.flag"
Which instructs it to delete the file that indicates that the first download in the list has already happened.
This method works, but it means that the first run is slow, and the rpg file effectively exists in two different places on your android device. (and that is not even counting the third copy when the .rpg file is unlumped to a temporary location)
Is a better system even possible?
It would be better to avoid copying the rpg file (or rpgdir) to a different location on the sdcard. I assume when the apk is installed, its files are unpacked to private storage somewhere, but I have not been able to find this location.
There is a java command getFilesDir() to get the private storage location. sdl-android stores the location in an environment variable SECURE_STORAGE_DIR
nativeSetEnv( "SECURE_STORAGE_DIR", p.getFilesDir().getAbsolutePath() );
The value stored in the environment variable will actually be /data/data/com.hamsterrepublic.ohrrpgce.game/files and that folder contains only the libsdl settings file. The parent directory /data/data/com.hamsterrepublic.ohrrpgce.game contains one file named libs and two folders, cache and files
After further reading about "assets" and "resources" I am pretty sure that installing an apk does *not* unpack these files to any location in the filesystem. You have to use java calls to get this data, and if it is even slightly large, it has to be broken into chunks and the app is resposible for reconstructing the whole file from the chunks at runtime.
So I think we are unlikely to be able to do any better than what sdl-android is doing already.
Release builds
Apparently, release builds can be created by running
./build.sh release
And the resulting file will be project/bin/MainActivity-release-unsigned.apk
Then you must sign it using jarsigner using a command line something like:
jarsigner \ -sigalg MD5withRSA \ -digestalg SHA1 \ -keystore ~/path/to/your/private/key/filename.keystore \ -storepass "whatever your key password is" \ project/bin/MainActivity-release-unsigned.apk \ nameofyourkey mv project/bin/MainActivity-release-unsigned.apk project/bin/MainActivity-release-unaligned.apk zipalign -v 4 project/bin/MainActivity-release-unaligned.apk project/bin/MainActivity-release-signed.apk
However, in my testing so far, the release apk crashes right after displaying "Loading..." Here is the logcat from the crash http://pastebin.com/raw.php?i=msRWELzm By adding a bunch of debug statements I found that the crash happens in prepare_map -> loadmapstate_tilemap -> loadmap_tilemap -> hash_file -> strhash in the line:
hash += *cast(unsigned integer ptr, strp)