Saturday, September 29, 2012

An Introduction to Flash SDL

Emcmanus's Flash SDL (Simple DirectMedia Layer) using Alchemy V0.5 is very useful for porting C/C++ & SDL based games to Flash. Although Alchemy 2 (FlasCC) is coming soon, and it will have much better support for SDL, Emcmanus's simple Flash SDL library with Alchemy V0.5 just works fine for me now. So I think this simple tutorial for Flash SDL can still be helpful for some people.

1. Flash SDL Installation
You should correctly installed Alchemy V0.5 first. Then goto https://github.com/emcmanus/flashsdl, download the repository zip, it should be "emcmanus-flashsdl-04ce063.zip". Unzip, and
Copy "sdl/SDL.l.bc" to your Alchemy's lib directory, e.g., "D:\alchemy-cygwin-v0.5a\usr\local\lib",
(or within Cygwin "cp sdl/SDL.l.bc $ALCHEMY_HOME/usr/local/lib/")
Copy all header files in "sdl/include" to your Alchemy's include directory, e.g., "D:\alchemy-cygwin-v0.5a\usr\local\include"
(create this folder by yourself if it does exists, or within Cygwin "cp sdl/include/*.h $ALCHEMY_HOME/usr/local/include/")

Now let's test the sample project.
Open Cygwin Terminal, build the swc lib for our project:

cd /cygdrive/f/alchemy/emcmanus-flashsdl-04ce063/
source /cygdrive/d/alchemy-cygwin-v0.5a/alchemy-setup
alc-on  
gcc flashSDL.c -DFLASH -Isdl -lSDL -swc -O3 -o libSDL.swc
Run FlashDevelop and create a new AS3 project in the "emcmanus-flashsdl-04ce063" folder, set "flashsdl.as" as the document class, and add "libSDL.swc" to Library.


Finally build & test the project, you should see a black screen with SDL's mouse icon:


Let see the author's comments for porting your SDL application to FlashSDL:
"Porting your SDL application to FlashSDL
Perhaps this is best understood by example. Examine ./flashsdl.c. Most immediately you will have to refactor your C application's main loop to run iteratively in the tick() method, assuming you end up using the application scaffolding in ./src/.
Make sure you've properly built and installed FlashSDL by building the test application. Then try running your application's ./configure.
Once you've successfully compiled, try linking the resuling SWC with the AS3 side of your application (which should be built on ./src/).
Other Tips
You have to set the color depth of your application to 32 Bits per pixel (in your call to SDL_SetVideoMode)."

Basically, the .as files in "./src/" folder is used to fetch the SDL's pixel buffer and display this buffer using a Flash bitmap at each frame. The "flashsdl.c" should be your skeleton for your own C/C++ project. Just move the initializing code into the setup() function and refactor the mainloop into the tick() method. You can also make use of those already declared variables in that file, such as using "TMPFLASH_screen" as your main screen buffer.

2. Displaying BMP Images in Flash SDL
The code in this section is based on Lazy Foo' tutorial: http://lazyfoo.net/SDL_tutorials/lesson01/index2.php.

(Flash) SDL has build-in BMP image support. To let the Flash SDL load a BMP image, we need to modify some AS3 code:
in "\src\sdl\LibSDL.as"
internal var cLoader:CLibInit;
To
public var cLoader:CLibInit;
Then embed the image file and supply it to Alchemy - related AS code in modified "flashsdl.as":
[Embed(source="../hello.bmp",mimeType="application/octet-stream")]
public static var hellobmpClass:Class;
...
this.libSDL = new LibSDL();
this.libSDL.cLoader.supplyFile("hello.bmp", new hellobmpClass());
...
Now you can load the image "hello.bmp" form C side normally. Please find the full C side code for loading and displaying the image in my source code package for this tutorial.
After adding the image loading and displaying code on the C side, recompile you will see something like this:


3. Playing Sound in Flash SDL
The Flash SDL does not have sound support yet on the C side.There is a fork on github which tried to add sound support, https://github.com/kompjoefriek/flashsdl, but it is not usable yet and there is no updates for a long time. Noticing that most SDL applications are using SDL_mixer library instead of SDL_sound, I think a much wiser way is to use Flash's build in sound support instead of porting both libraries. You can also find my simple solution for playing "mp3" files in source code package (ALC_GE2D_PlaySound.c, flashSDL_sound.c), I just wrote some simple function calls to let the C function call the AS3 method for playing mp3.

4. True Type Font support and SDL_ttf in Flash SDL
Thanks to Emcmanus again, who has already ported the FreeType library to Flash, we can use the SDL_ttf library in Flash SDL to display true type font texts.

First, goto https://github.com/emcmanus/FlashFreeType, download the compiled lib in the repository zip,
Copy "freetype.l.bc" into "D:\alchemy-cygwin-v0.5a\usr\local\lib",
and
Copy all header files in ".\src\c\freetype-2.3.9\include" to "D:\alchemy-cygwin-v0.5a\usr\local\include".

Then download the source file of SDL_ttf, form http://www.libsdl.org/projects/SDL_ttf/, put SDL_ttf.h SDL_ttf.c into the same folder of "flashSDL_sound_font.c", and include both files in "flashSDL_sound_font.c" using
#include "SDL_ttf.h"
#include "SDL_ttf.c"
After added some testing code for displaying text in "flashSDL_sound_font.c"'s setup method (please find the related C code in this tutorial's source code package, which are all based on http://lazyfoo.net/SDL_tutorials/lesson07/index.php), build the swc using some command like:
g++ flashSDL_sound_font.cpp -DFLASH -Ifreetype -Isdl -lfreetype -lSDL -swc -O3 -o libSDL.swc
Also embed and supply the font file to C:
[Embed(source="../lazy.ttf",mimeType="application/octet-stream")]
public static var fontClass:Class;
...
this.libSDL.cLoader.supplyFile("lazy.ttf", new fontClass());
Recompile the swf, you will see the font displaying successfully:


Note: when using g++ to build the swc instead of gcc, you may need to edit some function declarations from "flashSDL_sound_font.c" to:
AS3_Val setup(void *data, AS3_Val args);//need arguments here!
AS3_Val quitApplication(void *data, AS3_Val args);//need arguments here!
AS3_Val tick(void *data, AS3_Val args);//need arguments here!
AS3_Val FLASH_getDisplayPointer(void *data, AS3_Val args);//need arguments here!

5. Fix the Arrow Keys Bug in Flash SDL
You may find that arrow key event will be ignored if you're using the precompiled "SDL.l.bc" provided by the author. This is very inconvenient since many game applications are heavily relying on arrow keys. To add arrow key support, find the "SDL_flashevents.c" file in "\emcmanus-flashsdl-04ce063\sdl\src\video\flash\" folder, goto the "FLASH_InitOSKeymap" function and add four lines for mapping arrow key events at the end of that function as below:
void FLASH_InitOSKeymap(_THIS)
{
...
keymap[SCANCODE_APOSTROPHE] = SDLK_QUOTE;

keymap[38] = SDLK_UP;
keymap[40] = SDLK_DOWN;
keymap[37] = SDLK_LEFT;
keymap[39] = SDLK_RIGHT;
}
Recompile the "SDL.l.bc"
In sdl/:
make -f Makefile.flash clean all;
cd F:/alchemy/emcmanus-flashsdl-04ce063/sdl
Copy the compiled "SDL.l.bc" (F:\alchemy\emcmanus-flashsdl-04ce063\sdl) into "D:\alchemy-cygwin-v0.5a\usr\local\lib" and replace the old one. You can also find the updated "SDL.l.bc" in my source code package. If you don't want to recompile the whole Flash SDL library, simply copy mine and replace the old one.
After that, recompile the swc and your Flash project, arrow keys should work then.

6. Fix the supplyFile bug in Alchemy
This bug had been described by me here: http://forums.adobe.com/thread/942556. The usual problem brought by the bug is that game saves can only be loaded once (if you use fopen on the C side) in each game session. You always need to restart the flash player (and hence your game) to load game saves if you have used the first opportunity. The bug is caused by that using "supplyFile" twice with the same file path won't work if you ever used "fopen" between the two "supplyFile"s.

To fix this bug, you need to modify the generated .as file by Alchemy as described in my old post:
http://bruce-lab.blogspot.sg/2011/01/adobe-alchemy-hacks-compile-as-source.html

First download the fixed version of "alc-asc" here: http://flaswf.googlecode.com/svn/trunk/QuickAlchemy/Hack/SWC/, put it into your "alchemy/achacks" folder, and search & replace the string "F:/alchemy/" to your Alchemy path (mine is "D:/alchemy-cygwin-v0.5a/").
When building the swc using Alchemy, keep the "XXXX.achacks.as" file, then open it, commentize the line "if(!res)" in the function "fetch", so the patched function should look like this:
private function fetch(path:String):Object
{
var res:Object = statCache[path];

//if(!res)
{
var gf:ByteArray = gfiles[path];
...
Finally, recompile the ".as" file into the swc.
alc-asc 6956.achacks.as libSDL.swc
With this patched swc, "supplyFile" will work well.

Links:
1. The source code package for this tutorial:
https://flaswf.googlecode.com/svn/trunk/flaswfblog/Tutorials/FlashSDL

2. Two example SDL games ported to Flash (with full source code):

Infinite Balls:
Demo: https://en.mochimedia.com/community/games/Bruce_Jawn/infinite-balls
Source Code: https://flaswf.googlecode.com/svn/trunk/Games/InfiniteBalls

Sword Girl:
Demo: https://en.mochimedia.com/community/games/Bruce_Jawn/_v52410
Source Code: https://flaswf.googlecode.com/svn/trunk/Games/GirlSwordFlash

3. Array @ARGV missing the @ in argument 1 of shift() at problem
http://forums.adobe.com/message/3892045

4. Flash SDL FlasCC version:
https://github.com/alexmac/alcextra 

Sponsors