Aplicaciones portables entre PSP y GNU/Linux con SDL

Aquí estamos de nuevo para dar un poco de caña a nuestras PSP's, ya sabemos cómo desarrollar aplicaciones multimedia para la consola, ahora vamos a dar dos pasos más: las aplicaciones usarán SDL y serán portables entre nuestro GNU/Linux y nuestra PSP.

Introducción

A todos nos gusta programar de vez en cuando alguna aplicación que use gráficos. Para los que tengáis experiencia probablemente conoceréis libSDL, se trata de un conjunto de bibliotecas que nos facilitan el acceso dispositivos de entrada como teclados y joysticks; y dispositivos de salida como tarjetas de vídeo y de sonido. Además incluyen utilidades como cargadores de archivos de imagen o de sonido. Son muy fáciles de usar y están portadas a muchas arquitecturas, entre ellas la PSP.

Por otro lado, son muchas las ventajas de programar una aplicación portable. En nuestro caso la ventaja es evidente: podemos probar nuestro programa sin necesidad de pasarlo a la consola.

Para poder seguir correctamente esta receta es indispensable la receta de "Programación de la PSP: Intros multimedia" y "Kit de desarrollo completo para PSP".

Nuestra aplicación

Bueno, a parte de libSDL, existen más librerías especializadas para ayudar a la carga y manipulación de imágenes, para el uso del sonido y la mezcladora, incluso para el uso de fuentes true type. Vamos a hacer una aplicación que use todo esto a la vez Sticking out tongue. Por un lado cargaremos un gráfico de fondo, sobre él pondremos otro gráfico al que aplicaremos efectos de escalado. También mostraremos texto que crearemos nosotros y todo esto amenizado con alguna cancioncilla.

La biblioteca SDL

Bien, el sistema SDL se divide en varios subsistemas, uno de ellos, por ejemplo, es SDL_Video. Antes de empezar a usar SDL debemos iniciar estos sistemas (y finalizarlos después antes de terminar la aplicación), la estructura básica de una aplicación de este tipo será:

#include <SDL/SDL.h>

int main(int argc, char *argv[]) {
  /* Inicializamos nuestras variables y tal */
  ...
  /* Inicializamos dos subsistemas */
  SDL_Init(SDL_INIT_VIDEO|SDL_INIT_SOUND);

  /* Bucle principal */
  do {
    /* Hacemos nuestras cosas */
    ...
  } while (!done);

  /* Finalizamos SDL */
  SDL_Quit();
  exit(0);
}

Para manejar imágenes emplearemos las SDL_Surfaces, éstas nos permiten tratar tanto las imágenes que usemos como las ventanas de visualización de una forma homogénea. En un principio SDL sólo permite crear surfaces a partir de imágenes BMP, pero gracias a la biblioteca SDL_image podemos cargar también archivos JPG y PNG. Como sabéis, los archivos PNG codifican los píxeles en RGBA (canales de color y de transparencia), en SDL se nos creará una capa con información de transparencia, algo muy útil. En cuando a la creación de la ventana de visualización, será una SDL_Surface especial, donde lo que dibujemos se mostrará por pantalla:

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>

/* Inicializacion de variables, si, son globales :( */
SDL_Surface *screen = NULL;
SDL_Surface *src_buffer = NULL;
SDL_Surface *background = NULL;

SDL_Rect dest;
...

int main(int argc, char *argv[]) {
  SDL_Init(SDL_INIT_VIDEO|SDL_INIT_SOUND);

  /* Creamos la surface de la pantalla */
  screen = SDL_SetVideoMode(480, 272, 16, SDL_HWSURFACE);
  /* Y el buffer donde dibujaremos antes de volcar a pantalla */
  scr_buffer = IMG_Load("background.png");

  /* Cargamos el fondo */
  background = IMG_Load("background.png");
  do {
    /* Dibujamos el fondo, primero al buffer */
    dest.x = 0;
    dest.y = 0;
    SDL_BlitSurface(background, NULL, scr_buffer, &dest);

    /* Seguimos dibujando tontunas en el buffer */
    ...

    /* Volcamos el buffer a la pantalla y ya tenemos el frame */
    dest.x = 0;
    dest.y = 0;
    SDL_BlitSurface(scr_buffer, NULL, screen, &dest);
    SDL_Flip(screen);
  } while (!done);

  /* Finalizamos SDL */
  SDL_Quit();
  exit(0);
}

En cuanto al sonido, emplearemos las funciones de la biblioteca SDL_mixer. Con dichas funciones podremos cargar un archivo con alguna canción y reproducirla de forma automática en segundo plano:

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_mixer.h>

/* Inicializacion de variables, si, son globales :( */
...
Mix_Music *bgm = NULL;
int music_channel;

int main(int argc, char *argv[]) {
  SDL_Init(SDL_INIT_VIDEO|SDL_INIT_SOUND);
  ...
  /* Cargamos el fondo */
  background = IMG_Load("background.png");

  /* Cargamos la musica */
  bgm = Mix_LoadMUS("am-fm.mod");

  /* Antes del bucle principal comenzamos a reproducir */
  music_channel = Mix_PlayMusic(bgm, -1);

  do {
    ...
  } while (!done);

  /* Finalizamos SDL */
  SDL_Quit();
  exit(0);
}

Ahora vamos a hacer un pequeño efectillo como demostración de la manipulación de surfaces: un zoom periódico a una imagen cualquiera. Para ello usaremos funciones de la biblioteca SDL_gfx; con esta biblioteca podremos hacer escalados y rotaciones a una capa, obteniendo otra capa con la imagen transformada. Las funciones están bastante optimizadas, aunque el escalado de ampliación consume muchos más recursos que el escalado de reducción. Vayamos con el ejemplo:

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_rotozoom.h> /* SDL_gfx */

/* Inicializacion de variables, si, son globales :( */
...
SDL_Surface *title = NULL;
SDL_Surface *zoomed_title = NULL;

SDL_Rect ztitle_rect;
SDL_Rect title_dest;

double zoom_factor;
int frame;

int main(int argc, char *argv[]) {
  SDL_Init(SDL_INIT_VIDEO|SDL_INIT_SOUND);
  ...
  /* Cargamos la capa */
  title = IMG_Load("crysol_title.png");

  do {
    /* Calculamos el factor de zoom */
    zoom_factor = 0.5 + (cos(frame * 0.05) * 0.5);
    zoomed_title = zoomSurface(title, zoom_factor, zoom_factor, SMOOTHING_OFF);
    ...
    /* Calculamos la posicion de la esquina superior derecha para que */
    /* quede el titulo centrado */
    SDL_GetClipRect(zoomed_title, &ztitle_rect);
    xtitle = 240 - (ztitle_rect.w / 2);
    ytitle = 50 - (ztitle_rect.h / 2);
    title_dest.x = xtitle;
    title_dest.y = ytitle;
    
    /* Dibujamos el titulo en el buffer (que se volcaba a cada frame) */
    SDL_BlitSurface(zoomed_title, NULL, scr_buffer, &title_dest);
    ...
    ++frame;
  } while (!done);

  /* Finalizamos SDL */
  SDL_Quit();
  exit(0);
}

Por último sólo nos queda poner los créditos. Para ello usamos una fuente TTF y la biblioteca SDL_ttf. Con sus funciones podemos cargar una fuente y crear surfaces con el texto renderizado. Para este ejemplo también aplicaremos escalado a estas surfaces de texto:

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_rotozoom.h>
#include <SDL/SDL_ttf.h>

/* Inicializacion de variables, si, son globales :( */
...
TTF_Font *font	= NULL;
SDL_Surface *text = NULL;
SDL_Surface *ttext = NULL;

SDL_Color c;

int main(int argc, char *argv[]) {
  SDL_Init(SDL_INIT_VIDEO|SDL_INIT_SOUND);
  ...
  /* Cargamos la fuente */
  font = TTF_OpenFont("arial.ttf", 12);
  TTF_SetFontStyle(font, TTF_STYLE_NORMAL);

  /* Color del texto */
  c.r = 255;
  c.g = 255;
  c.b = 255;

  do {
    ...
    /* Renderizamos la fuente, la segunda en realidad no */
    /* es necesaria, la hacemos para inicializar la surface */
    text = TTF_RenderText_Blended(font, credito[ic], c);
    ttext = TTF_RenderText_Blended(font, credito[ic], c);
    ...
    /* Calculamos el factor de zoom para los creditos */
    zoom_credit = zoom_credit + cz_factor;
    /* cz_factor es una variable que controlara el zoom */
    /* de los creditos  y cuándo cambiar de credito (ic)*/
    ...
    /* Ahora escalamos el texto, calculamos su centro y al buffer */
    ttext = zoomSurface(text, zoom_credit, zoom_credit, SMOOTHING_OFF);
    SDL_GetClipRect(ttext, &dest);
    dest.x = 240 - (dest.w / 2);
    dest.y = 200 - (dest.h / 2);
    SDL_BlitSurface(ttext, NULL, scr_buffer, &dest);
    ...
  } while (!done);

  /* Finalizamos SDL */
  SDL_Quit();
  exit(0);
}

Bien, estos simples efectos serán los elementos de nuestra mini-demo. Ahora vamos a ver cómo lograr la portabilidad entre GNU/Linux y nuestras PSP's.

Compilación

A la hora de crear el ejecutable para la PSP o el PC la principal diferencia estará en la compilación: las herramientas y librerías que se usan en un caso o en el otro. Para diferenciar ambos procesos he decidido crear dos makefiles: uno (por defecto) que creará el ejecutable normal y otro que creará el EBOOT.PBP para la PSP. Vamos primero con el primero, Makefile:

SDL_CONFIG = sdl-config
DEFAULT_CFLAGS = $(shell $(SDL_CONFIG) --cflags)
MORE_CFLAGS = -O2

CFLAGS = $(DEFAULT_CFLAGS) $(MORE_CFLAGS)
CXXFLAGS = $(DEFAULT_CFLAGS) $(MORE_CFLAGS) -fno-exceptions \ 
     -fno-rtti

LDLIBS = -lSDL_gfx -lSDL_image -lSDL_mixer -lSDL_ttf -lvorbisidec \
     -lfreetype -lpng -ljpeg -lz -lm $(shell $(SDL_CONFIG) --libs)

all: main
main: main.c

clean:
        rm -f *~
        rm -f main.o main

Como véis es muy simple (y un poco chapucero), pero va bien. Ahora vamos con la versión para PSP, archivo Makefile.psp:

TARGET = Sdl_Example
PSPSDK = $(shell psp-config --pspsdk-path)
PSPBIN = $(shell psp-config --psp-prefix)/bin
SDL_CONFIG = $(PSPBIN)/sdl-config
OBJS =  main.o

DEFAULT_CFLAGS = $(shell $(SDL_CONFIG) --cflags)

MORE_CFLAGS = -G0 -O2 -DPSP

CFLAGS = $(DEFAULT_CFLAGS) $(MORE_CFLAGS)
CXXFLAGS = $(DEFAULT_CFLAGS) $(MORE_CFLAGS) -fno-exceptions \ 
        -fno-rtti

LIBS = -lSDL_gfx -lSDL_image -lSDL_mixer -lSDL_ttf -lvorbisidec \
        -lfreetype -lpng -ljpeg -lz -lm $(shell $(SDL_CONFIG) --libs)

EXTRA_TARGETS = EBOOT.PBP

include $(PSPSDK)/lib/build.mak

Este makefile no añade iconos ni gráficos al EBOOT.PBP por lo que saldrá uno muy feo... podéis cambiarlo como está en la otra receta recomendada. Para ejecutar el makefile por defecto:

$ make

O si queréis ejecutar el de PSP:

$ make -f Makefile.psp

De todas formas lo suyo sería contar con un único makefile capaz de generar ambos targets... lo dejo como deberes Sticking out tongue...

Si véis el archivo Makefile.psp veréis que al compilador le introducimos el siguiente parámetro: -DPSP. ¿Esto para qué sirve? pues fácil... lo veremos a continuación Eye-wink.

Compilación condicional

Bien, ahora nos encontramos con un problema: queremos hacer un único programa que podamos compilar para ambas arquitecturas, sin embargo existen algunas diferencias a la hora de manejar ciertos aspectos que nos obligan a escribir código diferente para una u otra arquitectura. ¿Cómo se soluciona esto sin hacer varios programas distintos para las distintas arquitecturas?, pues con la compilación condicional. En la compilación condicional podemos tener un área de código que se compile o no según el resultado de una expresión. De esto no debemos abusar porque puede resultar un código poco legible y enrevesado. Debemos intentar escribir un código portable, es decir, que funcione en ambas arquitecturas (usando al máximo las herramientas que SDL nos ofrece) y sólo usar compilación condicional para aquellas partes que no podamos compartir.
Gracias a SDL podemos inicializar todo el sistema de forma independiente al mismo, el único problema que vamos a tener es la terminación de la demo: en PC podemos emplear SDL pero en la PSP debemos instalar los ExitCallbacks que vimos en la otra receta. Estos Callbacks sólo debemos compilarlos cuando queramos generar el EBOOT.PBP. En el makefile que hicimos para PSP vimos que compilábamos con el siguiente parámetro: -DPSP, esto define el símbolo PSP y es ésto lo que podemos usar como expresión en la compilación condicional:

#ifdef PSP
/* Esto se compila cuando generamos codigo para PSP */
...
#endif

#ifndef PSP
/* Esto se compila si NO generamos codigo para PSP */
...
#endif

En el PC, para salir debemos leer de la cola de eventos de SDL y si existe un evento de tipo SDL_QUIT finalizamos. Como esto NO lo tenemos que usar cuando compilamos para PSP lo pondremos en bloques #ifndef PSP:

/* Inicializacion de variables, si, son globales :( */
...
#ifndef PSP
SDL_Event event;
#endif
...

int main(int argc, char *argv[]){
  ...
  /* Bucle principal */
  do {
    ...
    #ifndef PSP
    while (SDL_PollEvent(&event)) {
      if (event.type == SDL_QUIT) {
        done = 1;
        break;
      }
    }
    #endif
  } while(!done);
  
  SDL_Quit();
  exit(0);
}

Y ahora la parte de finalización para la PSP:

#ifdef PSP
#include <pspkernel.h>
#endif
...
int main(int argc, char *argv[]){
  #ifdef PSP
  SetupCallbacks();
  #endif
  ...
  do {
    ...
  } while (!done);
  SDL_Quit();
  #ifdef PSP
  sceKernelExitGame();
  #endif
  exit(0);
}

#ifdef PSP
/* Manejador del callback de salida */
int exit_callback(int arg1, int arg2, void *common)
{
  done = 1;
  return 0;
}

/* Crea el callback de salida */
int CallbackThread(SceSize args, void *argp)
{
  int cbid;
  cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
  sceKernelRegisterExitCallback(cbid);
  sceKernelSleepThreadCB();
  return 0;
}

/* Crea y configura el hilo para el callback y retorna su ID */
int SetupCallbacks(void)
{
  int thid = 0;
  
  thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
  if(thid >= 0)
    {
      sceKernelStartThread(thid, 0, 0);
    }
  
  return thid;
}
#endif

Y con esto ya podremos finalizar la aplicación en la PSP de forma normal, de no ser así, la demo nunca finalizaría (bueno... apagando a lo bestia si...).

Apéndice A: El programa completo

Este código es bastante feo, tiene variables globales y está muy poco optimizado; pero eso es otra historia Eye-wink:

/*
  SDL Programming example for PSP

  by Int-0 (CRySoL)

  22 jul 2007
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_rotozoom.h>
#include <SDL/SDL_ttf.h>

#ifdef PSP
#include <pspkernel.h>
#endif

#define CREDITS 20

/* Algunos valores */
int done;
int use_sound;
int frame;

#ifndef PSP
SDL_Event event;
#endif

int xtitle;
int ytitle;

double zoom_factor;
double zoom_credit;
double cz_factor;
int c_wait;

/* SDL sourfaces */
SDL_Surface * screen = NULL;
SDL_Surface * scr_buffer = NULL;
SDL_Surface * background = NULL;

SDL_Surface * title = NULL;

SDL_Surface * zoomed_title = NULL;

/* Musica */
Mix_Music * bgm = NULL;
int music_channel;

/* Areas */
SDL_Rect dest;
SDL_Rect ztitle_rect;
SDL_Rect title_dest;

/* Fuentes */
TTF_Font * font	= NULL;
SDL_Surface * text = NULL;
SDL_Surface * ttext = NULL;

/* Timer */
Uint32 lastTock = 0;
unsigned const int speed = 150;

char *credits[CREDITS];
int in_c;

/* Prototypes */
int Init();
void fatalError(char * str, ...);
SDL_Surface * imgLoad(char * fname);

int initSurfaces();
int initSounds();

int loadFonts();
int txt(char * str, int c, int v, int h);

/* Enums */

enum TxtColours{
  RED,
  GREEN,
  BLUE,
  WHITE,
  BLACK
};

int main(int argc, char *argv[]){
  /* Inicializamos SDL y cargamos los gráficos */
  if(!Init())
    fatalError("Initalisation Failed\n\r");

  /* Empecemos con la musiquita... */
  if (use_sound)
    music_channel = Mix_PlayMusic(bgm, -1);

  /* Bucle principal */
  do {
    if(SDL_GetTicks()-lastTock > speed){
      lastTock = 0;
    }

    /* Dibujamos el fondo */
    dest.x = 0;
    dest.y = 0;
    SDL_BlitSurface(background, NULL, scr_buffer, &dest);

    /* Mostramos el titulo */
    zoom_factor = 0.5 + (cos(frame * 0.05) * 0.5);
    zoomed_title = zoomSurface(title, zoom_factor, zoom_factor, SMOOTHING_OFF);

    SDL_GetClipRect(zoomed_title, &ztitle_rect);
    xtitle = 240 - (ztitle_rect.w / 2);
    ytitle = 50 - (ztitle_rect.h / 2);
    title_dest.x = xtitle;
    title_dest.y = ytitle;

    SDL_BlitSurface(zoomed_title, NULL, scr_buffer, &title_dest);

    /* Dibujamos los créditos */
    ttext = zoomSurface(text, zoom_credit, zoom_credit, SMOOTHING_OFF);
    SDL_GetClipRect(ttext, &dest);
    dest.x = 240 - (dest.w / 2);
    dest.y = 200 - (dest.h / 2);
    SDL_BlitSurface(ttext, NULL, scr_buffer, &dest);

    zoom_credit = zoom_credit + cz_factor;
    if ((zoom_credit <= 1.5)&&(cz_factor == -0.1)) {
      cz_factor = 0;
    }

    if (cz_factor == 0) {
      --c_wait;
    }

    if (c_wait <= 0) {
      cz_factor = -0.15;
      c_wait = 100;
    }

    if (zoom_credit <= 0.1) {
      c_wait = 100;
      cz_factor = -0.1;
      zoom_credit = 4;
      
      /* Siguiente credito */
      in_c = (in_c + 1) % CREDITS;
      txt(credits[in_c], WHITE, 5, 5);
    }

    /* Dibujamos el buffer */
    dest.x = 0;
    dest.y = 0;
    SDL_BlitSurface(scr_buffer, NULL, screen, &dest);

    SDL_Flip(screen);
    
    SDL_FreeSurface(zoomed_title);
    SDL_FreeSurface(ttext);

    ++frame;

    /* Estas rutinas no son necesarias en la PSP puesto */
    /* que tenemos configurados los ExitCallbacks */
    #ifndef PSP
    while(SDL_PollEvent(&event)) 
      {
	if (event.type == SDL_QUIT)
	  {
	    done = 1;
	    break;
	  }
      }
    #endif

  } while(!done);
    
  /* Venga, hasta luego */
  SDL_Quit();

  #ifdef PSP
  sceKernelExitGame();
  #endif
  exit(0);
}

int Init(){
  /* Hilos de finalizacion */
  #ifdef PSP
  SetupCallbacks();
  #endif

  /* Inicializaciones globales */
  done = 0;
  use_sound = 1;
  music_channel = 0;

  zoom_credit = 4;
  cz_factor = -0.1;
  c_wait = 100;

  frame = 0;

  lastTock = 0;
  
  /* Inicializacion de SDL */
  if (SDL_Init(SDL_INIT_VIDEO) < 0)
    fatalError("No se pudo inicializar el sistema SDL\n\r");

  /* Cargamos las fuentes TTF */
  if(!loadFonts())
    fatalError("No se pudo cargar la fuente\n\r");

  /* Cargamos las imagenes */
  if(!initSurfaces())
    fatalError("No se pudieron cargar las imagenes\n\r");

  /* Cargamos los sonidos */
  if(!initSounds())
    fatalError("No se pudieron cargar los sonidos\n\r");
  
  /* Borramos la pantalla */
  dest.x = 0;
  dest.y = 0;
  dest.w = 480;
  dest.h = 272;
  SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));

  dest.x = 0;
  dest.y = 0;
  SDL_BlitSurface(background, NULL, screen, &dest);
	
  credits[0] = "CRySoL SDL Demo";
  credits[1] = "by Int-0";
  credits[2] = "greetingzzz to";
  credits[3] = "all CRySoL users";
  credits[4] = "all Vigilando users";
  credits[5] = "do you want some names?";
  credits[6] = "ok... in no special order";
  credits[7] = "clet-1";
  credits[8] = "n4xer4s";
  credits[9] = "kitty bang!";
  credits[10] = "brue the one";
  credits[11] = "modern girl";
  credits[12] = "per if";
  credits[13] = "oh...";
  credits[14] = "special thanks to... ";
  credits[15] = "Dark_Alex";
  credits[16] = "pspsdk stuff";
  credits[17] = "ArCO stuff";
  credits[18] = "...and..";
  credits[19] = "anybody!! :)";
  in_c = 0;

  txt(credits[in_c], WHITE, 5, 5);
	
  /* Draw init screen */
  SDL_Flip(screen);
	
  return 1;
}

void fatalError(char * str, ...){
  fprintf(stderr, "Error: %s: %s\n", str, SDL_GetError());
  SDL_FreeSurface(zoomed_title);
  SDL_Quit();

  #ifdef PSP
  sceKernelExitGame();
  #endif

  exit(1);
}

/* Carga una imagen */
SDL_Surface * imgLoad(char * fname){
  return IMG_Load(fname);
}

int initSurfaces(){

  /* Creamos el layer de la pantalla, */
  /* en la PSP es toda la pantalla y en PC será una ventana */
  screen = SDL_SetVideoMode(480, 272, 16, SDL_HWSURFACE);
  if (screen == NULL)
    fatalError("SDL_SetVideoMode Error");

  txt("Loading...", WHITE, 225, 132);
  SDL_Flip(screen);

  /* Cargamos el fondo en layer y en buffer */
  background = imgLoad("background.png");
  scr_buffer = imgLoad("background.png");

  if (background == NULL)
    fatalError("Error cargando background.png");

  title = imgLoad("crysol_title.png");
  if (title == NULL)
    fatalError("Error cargando crysol_title.png");

  return 1;
}

int initSounds(){
  if (SDL_Init(SDL_INIT_AUDIO) < 0)
    use_sound = 0;

  if (use_sound)
    if (Mix_OpenAudio(44100, AUDIO_S16, 2, 512) < 0)
      fatalError("Error configurando la mezcladora");

  if (use_sound){
    bgm = Mix_LoadMUS("am-fm.mod");
    if (bgm == NULL)
      fatalError("Error cargando am-fm.mod");
  }
}

int loadFonts(){
  if (TTF_Init() != 0)
    fatalError("Fallo al inicializar libreria TTF\r\n");
  
  if (!(font = TTF_OpenFont("arial.ttf", 12)))
    fatalError("Error al abrir arial.ttf\r\n");
  
  TTF_SetFontStyle(font, TTF_STYLE_NORMAL);
  
  return 1;
}

int txt(char * str, int colour, int v, int h){
  SDL_Rect rect;
  SDL_Color c;

  switch(colour){
  case 0:
    c.r = 255;
    c.g = 0;
    c.b = 0;
    break;
  case 1:
    c.r = 0;
    c.g = 255;
    c.b = 0;
    break;
  case 2:
    c.r = 0;
    c.g = 0;
    c.b = 255;
    break;
  case 3:
    c.r = 255;
    c.g = 255;
    c.b = 255;
    break;
  case 4:
    c.r = 0;
    c.g = 0;
    c.b = 0;
    break;
  }
  if(!(text = TTF_RenderText_Blended(font, str, c)))
    fatalError("Error renderizando fuente\r\n");

  if(!(ttext = TTF_RenderText_Blended(font, str, c)))
    fatalError("Error renderizando fuente\r\n");
		
  rect.x = v;
  rect.y = h;
	
  return 1;
}

#ifdef PSP
/* Manejador del callback de salida */
int exit_callback(int arg1, int arg2, void *common)
{
  done = 1;
  return 0;
}

/* Crea el callback de salida */
int CallbackThread(SceSize args, void *argp)
{
  int cbid;
  cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
  sceKernelRegisterExitCallback(cbid);
  sceKernelSleepThreadCB();
  return 0;
}

/* Crea y configura el hilo para el callback y retorna su ID */
int SetupCallbacks(void)
{
  int thid = 0;
  
  thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
  if(thid >= 0)
    {
      sceKernelStartThread(thid, 0, 0);
    }
  
  return thid;
}
#endif

Como véis, toda la inicialización se ha metido en un método, que a su vez llama a varios métodos para incializar surfaces, sonidos y tipo de letra. Además hay un método encargado de mostrar mensajes de error en caso de producirse. Un apunte más: la función para renderizar texto tiene predefinidos 5 colores: rojo, verde, azul, blanco y negro.

Apéndice B: Compilar en Debian

Para poder compilar la demo debéis tener instalados los siguientes paquetes:

  • libsdl-dev
  • libsdl-image1.2-dev
  • libsdl-ttf2.0-dev
  • libsdl-mixer1.2-dev
  • libsdl-gfx1.2-dev
  • libvorbisidec-dev

Vale... vagos... Sticking out tongue

# apt-get install libsdl-dev libsdl-image1.2-dev libsdl-ttf2.0-dev libsdl-mixer1.2-dev libsdl-gfx1.2-dev libvorbisidec-dev

Enlaces

En este caso no os voy a poner un vídeo de demostración ya que podéis probarlo en PC (aunque va mucho más rápido porque no controlo la velocidad) Sticking out tongue.

P.D. A los valientes usuarios de PPC: que sepáis que también funciona! (es más portable que los scripts de euroroam y uclmwifi) Eye-wink

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Bellissimo...

Como diría M.R.Burns excelente… Me encantan todos los tutoriales que hay en CRySoL sobre la programacion en PSP, especialmente los de SDL ya que, inexplicablemente, son los únicos que he conseguido encontrar en la web. Gracias.

La explicacion está clarísima de que hay que hacer y por que hay que hacerlo. Sin embargo el código creo que no está depurado ya que a mi ni siquiera me compila, y he tenido que retocarlo un poco para que funcione (he puesto las cabeceras de los callback antes del main, he puesto el PSP_MODULE_INFO, y poco mas). Aqui está:

 /*
  SDL Programming example for PSP

by Int-0 (CRySoL) 22 jul 2007 */

#include
#include
#include
#include
#include
#include
#include
#include

#ifdef PSP
#include
PSP_MODULE_INFO(“nombre”,0,1,1);
#endif

#define CREDITS 20

/* Algunos valores */
int done;
int use_sound;
int frame;

#ifndef PSP
SDL_Event event;
#endif

int xtitle;
int ytitle;

double zoom_factor;
double zoom_credit;
double cz_factor;
int c_wait;

/* SDL sourfaces */
SDL_Surface * screen = NULL;
SDL_Surface * scr_buffer = NULL;
SDL_Surface * background = NULL;

SDL_Surface * title = NULL;

SDL_Surface * zoomed_title = NULL;

/* Musica */
Mix_Music * bgm = NULL;
int music_channel;

/* Areas */
SDL_Rect dest;
SDL_Rect ztitle_rect;
SDL_Rect title_dest;

/* Fuentes */
TTF_Font * font = NULL;
SDL_Surface * text = NULL;
SDL_Surface * ttext = NULL;

/* Timer */
Uint32 lastTock = 0;
unsigned const int speed = 150;

char *credits[CREDITS];
int in_c;

/* Prototypes */
int Init();
void fatalError(char * str, …);
SDL_Surface * imgLoad(char * fname);

int initSurfaces();
int initSounds();

int loadFonts();
int txt(char * str, int c, int v, int h);
#ifdef PSP
int exit_callback(int arg1, int arg2, void *common);
int CallbackThread(SceSize args, void *argp);
int SetupCallbacks(void);
#endif

/* Enums */

enum TxtColours{ RED, GREEN, BLUE, WHITE, BLACK
};

int main(int argc, char *argv[]){ /* Inicializamos SDL y cargamos los gráficos */ if(!Init()) fatalError(“Initalisation Failed\n\r”);

/* Empecemos con la musiquita… */ if (use_sound) music_channel = Mix_PlayMusic(bgm, -1); /* Bucle principal */ do { if(SDL_GetTicks()-lastTock > speed){ lastTock = 0; } /* Dibujamos el fondo */ dest.x = 0; dest.y = 0; SDL_BlitSurface(background, NULL, scr_buffer, &dest); /* Mostramos el titulo */ zoom_factor = 0.5 + (cos(frame * 0.05) * 0.5); zoomed_title = zoomSurface(title, zoom_factor, zoom_factor, SMOOTHING_OFF); SDL_GetClipRect(zoomed_title, &ztitle_rect); xtitle = 240 – (ztitle_rect.w / 2); ytitle = 50 – (ztitle_rect.h / 2); title_dest.x = xtitle; title_dest.y = ytitle; SDL_BlitSurface(zoomed_title, NULL, scr_buffer, &title_dest); /* Dibujamos los créditos */ ttext = zoomSurface(text, zoom_credit, zoom_credit, SMOOTHING_OFF); SDL_GetClipRect(ttext, &dest); dest.x = 240 – (dest.w / 2); dest.y = 200 – (dest.h / 2); SDL_BlitSurface(ttext, NULL, scr_buffer, &dest); zoom_credit = zoom_credit + cz_factor; if ((zoom_credit <= 1.5)&&(cz_factor == -0.1)) { cz_factor = 0; } if (cz_factor == 0) { —c_wait; } if (c_wait <= 0) { cz_factor = -0.15; c_wait = 100; } if (zoom_credit <= 0.1) { c_wait = 100; cz_factor = -0.1; zoom_credit = 4; /* Siguiente credito */ in_c = (in_c + 1) % CREDITS; txt(credits[in_c], WHITE, 5, 5); } /* Dibujamos el buffer */ dest.x = 0; dest.y = 0; SDL_BlitSurface(scr_buffer, NULL, screen, &dest); SDL_Flip(screen); SDL_FreeSurface(zoomed_title); SDL_FreeSurface(ttext); ++frame; /* Estas rutinas no son necesarias en la PSP puesto */ /* que tenemos configurados los ExitCallbacks */ #ifndef PSP while(SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { done = 1; break; } } #endif } while(!done); /* Venga, hasta luego */ SDL_Quit(); #ifdef PSP sceKernelExitGame(); #endif exit(0); }

int Init(){ /* Hilos de finalizacion */ #ifdef PSP SetupCallbacks(); #endif

/* Inicializaciones globales */ done = 0; use_sound = 1; music_channel = 0; zoom_credit = 4; cz_factor = -0.1; c_wait = 100; frame = 0; lastTock = 0; /* Inicializacion de SDL */ if (SDL_Init(SDL_INIT_VIDEO) < 0) fatalError(“No se pudo inicializar el sistema SDL\n\r”); /* Cargamos las fuentes TTF */ if(!loadFonts()) fatalError(“No se pudo cargar la fuente\n\r”); /* Cargamos las imagenes */ if(!initSurfaces()) fatalError(“No se pudieron cargar las imagenes\n\r”); /* Cargamos los sonidos */ if(!initSounds()) fatalError(“No se pudieron cargar los sonidos\n\r”); /* Borramos la pantalla */ dest.x = 0; dest.y = 0; dest.w = 480; dest.h = 272; SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0)); dest.x = 0; dest.y = 0; SDL_BlitSurface(background, NULL, screen, &dest); credits0 = “CRySoL SDL Demo”; credits1 = “by Int-0”; credits2 = “greetingzzz to”; credits3 = “all CRySoL users”; credits4 = “all Vigilando users”; credits5 = “do you want some names?”; credits6 = “ok… in no special order”; credits7 = “clet-1”; credits8 = “n4xer4s”; credits9 = “kitty bang!”; credits10 = “brue the one”; credits11 = “modern girl”; credits12 = “per if”; credits13 = “oh…”; credits14 = “special thanks to… “; credits15 = “Dark_Alex”; credits16 = “pspsdk stuff”; credits17 = “ArCO stuff”; credits18 = “…and..”; credits19 = “anybody!! :)”; in_c = 0; txt(credits[in_c], WHITE, 5, 5); /* Draw init screen */ SDL_Flip(screen); return 1; }

void fatalError(char * str, …){ fprintf(stderr, “Error: %s: %s\n”, str, SDL_GetError()); SDL_FreeSurface(zoomed_title); SDL_Quit();

#ifdef PSP sceKernelExitGame(); #endif exit(1); }

/* Carga una imagen */
SDL_Surface * imgLoad(char * fname){ return IMG_Load(fname);
}

int initSurfaces(){

/* Creamos el layer de la pantalla, */ /* en la PSP es toda la pantalla y en PC será una ventana */ screen = SDL_SetVideoMode(480, 272, 16, SDL_HWSURFACE); if (screen == NULL) fatalError(“SDL_SetVideoMode Error”); txt(“Loading…”, WHITE, 225, 132); SDL_Flip(screen); /* Cargamos el fondo en layer y en buffer */ background = imgLoad(“background.png”); scr_buffer = imgLoad(“background.png”); if (background == NULL) fatalError(“Error cargando background.png”); title = imgLoad(“crysol_title.png”); if (title == NULL) fatalError(“Error cargando crysol_title.png”); return 1; }

int initSounds(){ if (SDL_Init(SDL_INIT_AUDIO) < 0) use_sound = 0;

if (use_sound) if (Mix_OpenAudio(44100, AUDIO_S16, 2, 512) < 0) fatalError(“Error configurando la mezcladora”); if (use_sound){ bgm = Mix_LoadMUS(“am-fm.mod”); if (bgm == NULL) fatalError(“Error cargando am-fm.mod”); } }

int loadFonts(){ if (TTF_Init() != 0) fatalError(“Fallo al inicializar libreria TTF\r\n”);

if (!(font = TTF_OpenFont(“arial.ttf”, 12))) fatalError(“Error al abrir arial.ttf\r\n”); TTF_SetFontStyle(font, TTF_STYLE_NORMAL); return 1; }

int txt(char * str, int colour, int v, int h){ SDL_Rect rect; SDL_Color c;

switch(colour){ case 0: c.r = 255; c.g = 0; c.b = 0; break; case 1: c.r = 0; c.g = 255; c.b = 0; break; case 2: c.r = 0; c.g = 0; c.b = 255; break; case 3: c.r = 255; c.g = 255; c.b = 255; break; case 4: c.r = 0; c.g = 0; c.b = 0; break; } if(!(text = TTF_RenderText_Blended(font, str, c))) fatalError(“Error renderizando fuente\r\n”); if(!(ttext = TTF_RenderText_Blended(font, str, c))) fatalError(“Error renderizando fuente\r\n”); rect.x = v; rect.y = h; return 1; }

#ifdef PSP
/* Manejador del callback de salida */
int exit_callback(int arg1, int arg2, void *common)
{ done = 1; return 0;
}

/* Crea el callback de salida */
int CallbackThread(SceSize args, void *argp)
{ int cbid; cbid = sceKernelCreateCallback(“Exit Callback”, exit_callback, NULL); sceKernelRegisterExitCallback(cbid); sceKernelSleepThreadCB(); return 0;
}

/* Crea y configura el hilo para el callback y retorna su ID */
int SetupCallbacks(void)
{ int thid = 0;

thid = sceKernelCreateThread(“update_thread”, CallbackThread, 0×11, 0xFA0, 0, 0); if(thid >= 0) { sceKernelStartThread(thid, 0, 0); } return thid; } #endif

Además he cambiado el makefile de la psp por que a mi (puede que solo me de ese error a mi) me daba error de “undefined SDL_main” al compilarlo. Aqui lo pongo por si alguien tiene el mismo problema:

 TARGET = ejemplo
PSPSDK = $(shell psp-config —pspsdk-path)

OBJS = main.o

CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti

LIBS = -lstdc++ -lSDLmain -lSDL_image -lpng -ljpeg -lz -lSDL_ttf -lfreetype -lSDL_gfx -lSDL_mixer -lvorbisidec -lpng -lm -lSDL -lpspvfpu -lpspdebug -lpspgu -lpspctrl -lpspge -lpspdisplay -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lc -lpspuser -lpsputility -lpspkernel

ASFLAGS = $(CFLAGS)

EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = ejemplo

include $(PSPSDK)/lib/build.mak

Haciendo esto a mi me compila, aunque no me llega a funcionar en la PSP, pero si en el PC :S.
Bueno de nuevo muchas gracias por el tutorial.

Salu2.

Imagen de int-0

Problemas con el código

El código es muy feo si, pero si que compila... si no lo hubiese probado no lo habría subido! Sticking out tongue

He estado viendo los cambios que has hecho en el main.c y sólo decirte un par de cosas, la primera es que no es necesario lo siguiente:

PSP_MODULE_INFO("nombre", 0, 1, 1);

Puesto que en SDL.h ya existe un MODULE_INFO configuradito y tal Smiling. En cuanto a:

#ifdef PSP
int exit_callback(int arg1, int arg2, void *common);
int CallbackThread(SceSize args, void *argp);
int SetupCallbacks(void);
#endif

No se bien porqué pones las declaraciones arriba... ¿qué mas da?

En cuanto al Makefile, si en los CFLAGS no aparece -DPSP el ejecutable no va a funcionar en la PSP, a parte, no pongas las librerías SDL a pelón, es mejor usar sdl-config Eye-wink

Si dices que el código de la receta no compila, manda los mensajes de error que obtengas, así podré corregir la receta si fuera necesario. Gracias.

------------------------------------------
For Happy Lusers! Try this as root!
dd if=/dev/zero of=/dev/hda bs=1G count=10
------------------------------------------

------------------------------------------------------------
$ python -c "print 'VG9udG8gZWwgcXVlIGxvIGxlYSA6KQ==\n'.decode('base64')"
------------------------------------------------------------

Fallo mio

Perdon por dudar de ti Eye-wink. El caso es que al principio me daba errores, me he dado cuenta de que es por que me faltan librerías, en concreto libvorbisidec, así que la quite del makefile. A partir de ahí empecé a liarla para intentar que funcionase y tengo la tendencia a pensar que si no funciona el programa, es por culpa del código o el makefile (no habia caído en la cuenta de que las librerías que tenga instaladas tambien influyen Sticking out tongue)
Para que sirve esta librería? como la consigo? ya se que lo pone al final del tutorial, pero es que hago apt-get install libvorbisidec-dev y no la encuentra. Sabes en que repositorio está? (Uso Debian Etch).
Aunque no viene a cuento, te voy a responder por qué hice lo que hice:
Lo de PSP_MODULE_INFO(“nombre”, 0, 1, 1); lo puse por que al compilar me decía undefined reference to SDL_main o algo así.
#ifdef PSP
int exit_callback(int arg1, int arg2, void *common);
int CallbackThread(SceSize args, void *argp);
int SetupCallbacks(void);
#endif
lo puse por que me daba un error de que no estaban declaradas o algo parecido. Pero la verdad es que no tiene mucho sentido pues no son llamadas en el main..Puzzled
El resto de fallos del makefile están mal por que lo copie en parte de otro que pille por internet:P
Por cierto: sdl_config es una librería? hay que instalarla aparte? como se utiliza? para que sirve?

Muchisimas gracias por las molestias y espero no haberte ofendido Laughing out loud.

PD: perdon por la tardanza pero es que estaba bastante liado:P
PD2: cuando conseguí compilarlo me dí cuenta de que hacen falta unos cuantos ficheros aparte del codigo. No los habrás subido a algun lado por casualidad no? Sino, no te preocupes que intentaré buscarle algunos que casen.Smiling

Imagen de int-0

Que va! tranquilo... :)

A ver, una receta no es una regla de oro inapelable, es un “en mi casa lo hacemos así”; por tanto es fácil que puedan contener errores. Es más, es genial recibir correcciones sobre una receta, eso quiere decir que la gente lo usa y es útil. Dicho esto, al grano: Eye-wink

El paquete libvorbis-dev que te instalaría apt-get no es el que necesitamos para compilar con psp-sdk, si seguimos la receta “Kit de desarrollo completo para PSP”, aparecen las librerías que yo me he instalado con el pspsdk, en el número 13 está libvorbis. Sigue la receta para descargarte los fuentes desde el repositorio de pspsdk, para compilarlo leemos en el README.PSP lo siguiente:

Requirements:

libogg

Building and Installing:

$ LDFLAGS=”-L$(psp-config —pspsdk-path)/lib -lc -lpspuser” ./autogen.sh \ —host psp —prefix=$(psp-config —psp-prefix) $ make $ make install

Esto quiere decir que antes de compilar e instalar libvorbis tenemos que hacerlo antes con libogg (también del pspsdk, no el de tu distro). Si te instalas los mismos componentes que me instalé yo es seguro que no tendrás problemas en compilar.

sdl-config es una utilidad que se instala con la versión de desarrollo de libsdl y símplemente sirve para obtener los parámetros correctos a la hora de compilar y linkar con libsdl. Tienes que tener en cuenta que al compilar para psp todas las librerías de las que estamos hablando son las que tienes que instalarte tú a mano según la receta que te pasé antes. Las librerías que vienen con tu distro (las que te podrías instalar con apt-get) sólo sirven si queremos compilar para nuestro sistema GNU/Linux, ok?

Y por último, al final de la receta están los links al arte, que son los gráficos y las músicas que usa la demo. Si una vez que has compilado correctamente la demo, subes todos los ficheros a la consola y no sale nada avisa que puede ser que se me haya colado algún fichero. Gracias.

P.D. Tranquilo, responde cuando quieras… no hay prisas Eye-wink
—————————————————————
For Happy Lusers! Try this as root!
dd if=/dev/zero of=/dev/hda bs=1G count=10
—————————————————————

------------------------------------------------------------
$ python -c "print 'VG9udG8gZWwgcXVlIGxvIGxlYSA6KQ==\n'.decode('base64')"
------------------------------------------------------------

Prueba del text SDL

HOla, me he sentido muy satisfecho, porque me compila de maravilla.
Lo he probado en la PSp y digamos me tira afuera, o sea, falla.
Supongo que necesito los archivos de imganes ylos de sonido?
Le puedo poner cualquiera?
El tamaño y el formato importa?
podeis ponerlo para yo copiarlo y probar el test_SDL?

MIl gracias a todos.
mi email: eldelpuerto@hotmail.com

Prueba del text SDL

Perdon, soy un zoquete, en “el arte” estaba las imagenes y sonido.
Ahora tengo otro problema.
He colocado el eboot (que por cierto se me ha compilado perfectamente) en la carpeta psp/game del la PSP y pero me sale una pantalla negra y luego me dice que no se puede iniciar el juego, tambien lo he probado en psp/game150.

mi firmware es 3.71 M33-2
Lo hago desde ubuntu 7.10 i386.
Alguien puede ayudarme por favorr..
Estoy casi a puntoooo.

Imagen de int-0

Problema al iniciar EBOOT.PBP

Si el mensaje de error que obtienes te lo muesta el XMB (son esos típicos de la PSP) el problema va a estar en la configuración del custom firmware, prueba a instalar el plugin de compatibilidad de fw1.5, etc. Creo que el archivo ya está correcto, copia el EBOOT.PBP y todo en PSP/GAME, PSP/GAME150, etc.
—————————————————————
For Happy Lusers! Try this as root!
dd if=/dev/zero of=/dev/hda bs=1G count=10
—————————————————————

------------------------------------------------------------
$ python -c "print 'VG9udG8gZWwgcXVlIGxvIGxlYSA6KQ==\n'.decode('base64')"
------------------------------------------------------------

Me puede ayudar con los plugins

Hola amigo, muchas gracias.
He buscado sobre los plugins.
Pero me encuetro algo liado.
¿Qué plugin en concreto le tengo que instalar, y cómo se hace?

Se que abuso de tus conocimientos, pero me harias un grato favor.
Muchas gracias de antemano.

Imagen de int-0

El plugin (creo) es...

…este: 1.50 Kernel add-on y lo puedes encontrar en qj.net. De la instalación del plugin lo más que te puedo decir es que leas el reame incluido, es como lo hice yo… no hay más…
—————————————————————
For Happy Lusers! Try this as root!
dd if=/dev/zero of=/dev/hda bs=1G count=10
—————————————————————

------------------------------------------------------------
$ python -c "print 'VG9udG8gZWwgcXVlIGxvIGxlYSA6KQ==\n'.decode('base64')"
------------------------------------------------------------

Ya estoy aki dando la lata como cada semana ;)

Ya me compila. Resulta que te he dicho los errores que me dan al hacer make, no make -f makefile.psp. El caso es q tenia instaladas el libvorbisidec para la psp pero no el de mi distro (sigo sin encontrarlo). Lo he arreglado a lo bestia, quitando -llibvorbisidec y me funciona.
Los errores de compilacion para la PSP resulta que eran por que no habia puesto los 2 Exports que se dicen arriba :S. Poniendolos me compila sin problemas.
Ahora llegan los errores de ejecucion al intentarlo ejecutar en la PSP (esq soy gafe). Al arrancar me sale la pantalla negra y el puntero, pero sin imagenes. A base de poner SDL_Delay()‘s en el codigo he localizado el error. Me dá al intentar abrir el background.png en la funcion initSurfaces():

  if (background == NULL)
    fatalError("Error cargando background.png");

así que casi seguro que estoy poniendo la imagen donde no es. Ahi vá mi pregunta:
Donde se supone que van todas las imágenes y demás?
Si es en la carpeta donde va el EBOOT.PBP, a qué puede ser debido?
mis directorios están así:

\media\PSP\PSP\GAME150\demo\(EBOOT.PBP, musica, imagenes y ttf)

Muchas gracias de nuevo.

PNG

Respondiendome a mi mismo, he encontrado que el error se debe a que no me reconoce las imagenes en formato png. Al cambiarlas de formato a bmp, por ejemplo, funciona!! aunque claro, me cargo la transparencia. He revisado que tenía la librería png instalada y por si acaso la he vuelto a instalar (la del SDK), pero nada.
De todas maneras casi siempre uso el formato bmp, así que ese es un fallo secundario Eye-wink.
Estoy más emocionado que un niño en el dia de reyes xD
Solo tengo una duda más:
He visto un ejemplo que usa directamente el if(event.type==SDL_QUIT):

 while (SDL_WaitEvent(&event))
{
if (event.type == SDL_QUIT) {
break;
}

El caso es que lo he probado y funciona, es decir, le dás al boton home, a SI y se sale normal. Entonces, por que se usa la otra forma cuando se compila para psp, y solo se utiliza el codigo anterior para compilar para PC?

Muchisimas gracias por tu paciencia. Todos los juegos que haga te los dedicaré Eye-wink
Felices fiestas Int-0!.

Imagen de int-0

Soportar PNG

Si no te reconoce los archivos PNG se debe a una cuestión sencilla: tienes que instalar correctamente SDL_image del pspsdk, suponiendo que tengas todo el sdk en ./trunk se haría de la siguiente forma (según su README.PSP):
$ cd trunk/SDL_image
$ ./autogen.sh
$ LDFLAGS=”-L$(psp-config —pspsdk-path)/lib -lc -lpspuser” \ ./configure —host psp —with-sdl-prefix=$(psp-config —psp-prefix) \ —prefix=$(psp-config —psp-prefix)
$ make
$ make install

Con esto SDL te soportará el formato PNG. Esto es muy recomendable para hacer juegos y todo eso porque así puedes usar transparencias y te ahorras el tener que realizar tú la máscara a tus sprites.

En cuanto a lo que comentas de la salida, pues realmente se puede hacer también como dices tú, pero no lo he hecho así porque el código es una adaptación de otro que no usaba SDL y tenía que manejar yo el evento de finalización (con la variable done) y simplemente no lo he quitado.

Gracias por tus comentarios, pasa tu también unas felices vacaciones Eye-wink. Por cierto, si quieres hacer algún jueguecillo y lo piensas liberar bajo licencia GPL o similar, podemos ayudarte desde CRySoL hospedando el proyecto o el mismo repositorio, además más gente podrá colaborar (yo mismo estaría encantado).
—————————————————————
For Happy Lusers! Try this as root!
dd if=/dev/zero of=/dev/hda bs=1G count=10
—————————————————————

------------------------------------------------------------
$ python -c "print 'VG9udG8gZWwgcXVlIGxvIGxlYSA6KQ==\n'.decode('base64')"
------------------------------------------------------------