Tutorial de programacion de Nintendo DS. Dia 1 – Carga de fondos desde la FAT

Día 1. Carga de fondos “tileados” desde FAT.
 
 

Introduccion
 
Bueno, pasemos a las cosas serias. Como doy por supuesto que ya sabemos programar mínimamente bien en C (cuidado, que yo no soy ni de lejos un lumbreras), pasaremos a las cosas que se salen de lo normal en la DS, como la carga y manejo de fondos, sprites, interface de entrada y sonido. Lo demás como es C de toda la vida, pues casi no tiene sentido que lo explique yo aquí, habiendo cientos de tutoriales (mucho mejores de los que podría yo realizar) sobre programación en C.

El tema que tocare hoy es referente a la carga de fondos, usando uno de los modos nativos que soporta el motor 2D de la DS, los gráficos tileados. Los gráficos tileados no son más que gráficos que han sido previamente convertidos en un formato especial, compuesto de 3 partes, para ahorrar espacio. En el caso de la DS, estos gráficos se componen en el archivo de tiles, donde la imagen se ha cortado en bloques de 8×8 pixeles (1 tile) y se han guardado todos juntos. El siguiente archivo necesario es el de mapa de tiles (MAP) que indica a la DS donde colocar cada tile del otro archivo en la pantalla. Esto es útil de cara a ahorrar memoria (la DS tiene poco mas de 512kb de memoria de video para ambas pantallas), ya que si en nuestro fondo se repite mucho un tile (un ladrillo por ejemplo), el grafico de este ladrillo solo se guarda 1 vez y se carga 1 sola vez en la VRAM y luego el archivo de mapa le indica a la DS cuantas veces debe de colocar ese ladrillo en pantalla y donde, con lo cual, si en pantalla tenemos 20 ladrillos, en memoria solo habrá cargado 1, pero la ds gracias al sistema de tiles nos mostrara los 20, con el consecuente ahorro de recursos. La tercera y última parte del sistema de tileado es el archivo de paleta, que indica que colores usara nuestro fondo. Debéis de tener en cuenta que este tipo de fondos serán siempre a 256 colores (color indexado) para poder usarlos.

La última release del devKitPro incluye una fabulosa utilidad llamada GRIT, la cual convertirá nuestros archivos de imagen en los 3 archivos que necesitamos para que la DS “lo entienda”.
La mayoría de ejemplos sobre gráficos tileados se basan en integrar los gráficos una vez convertidos dentro de la propia ROM generada. Cosa buena, en una sola ROM todo solucionado y funcionando. Cosa mala, como tu proyecto ocupe mas de 4mb, olvídate de que funcione, dado que esa es la RAM que tiene disponible la DS para trabajar. Así que para evitar problemas futuros, más vale hacer las cosas ya bien desde el principio y aprender a cargar los fondos, sprites y demás desde la FAT (sistema de archivos de vuestra flashcard) y tener solo en la ROM el ejecutable, con lo cual tendremos más flexibilidad a la hora de trabajar.

Después de esta pequeña introducción, hoy vamos a aprender a:
1. Inicializar el sistema FAT y cargar archivos desde ella a la RAM
2. Inicializar el motor 2D de la DS y cargar en la VRAM un fondo
Ademas también os enseñare a convertir los graficos al formato “tileado” con la utilidad GRIT.
 
 

Codigo.

 
Este es el código de la lección de hoy:

//
// Leccion 1
// Carga de fondos "tileados" desde FAT
// Ejemplo por NightFox
//

// Includes NDS
#include <nds.h>
// Includes FAT
#include <fat.h>
// Includes comunes
#include <stdio.h>
#include <string.h>
#include <unistd.h>
 
 

// Main
int main(void) {

 // Inicializa el sistema de interrupciones
   irqInit();
   // y habilita el vblank para poder usar swiWaitForVblank()
   irqEnable(IRQ_VBLANK);

 // Inicializa el sistema FAT
 consoleDemoInit(); // Inicializa la consola de texto
 iprintf("Inicializando FAT.n");
 if (fatInitDefault()) {  // Intenta inicializar la FAT
  // Conseguido, continua
 } else {
  // Fallo. Deten el programa
  iprintf("Fallo en la inicializacion.n");
  iprintf("Programa detenido.n");
  // Bucle infinito. Fin del programa
  while(1) {
   swiWaitForVBlank();
  }
 }

 // Define los Buffers para almacenar los graficos
 char* BUFFER_TILES;  // Buffer para alamacenar los tiles
 char* BUFFER_MAP;  // Buffer para alamacenar el mapa
 char* BUFFER_PAL;  // Buffer para almacenar la paleta

 // Inicializa los buffers (asi evitamos cargar los datos vete a saber tu donde)
 BUFFER_TILES = NULL;
 BUFFER_MAP = NULL;
 BUFFER_PAL = NULL;

 // Variable para almacenar el path y el nombre de archivo
 char filename[256];

 // Carga el archivo de TILES en la RAM
 long tiles_size = 0;  // Alamcenamos el tamaño del archivos de tiles
 FILE* tiles_file;  // Referencia al archivo de tiles
 sprintf(filename, "leccion01/fondo.img"); // Guarda el path + archivo
 tiles_file = fopen(filename, "rb");    // Y abre el archivo (modo lectura)

 if (tiles_file) {  // Si el archivo existe
  // Obten el tamaño del archivo
  fseek(tiles_file, 0, SEEK_END);
  tiles_size = ftell(tiles_file);
  rewind(tiles_file);
  // Reserva el espacio en RAM
  BUFFER_TILES = (char*) calloc (tiles_size, sizeof(char));
  if (BUFFER_TILES == NULL) {  // Si no hay suficiente RAM libre
   iprintf("RAM insuficiente.n");
   iprintf("Archivo: %ld bytesn", tiles_size);
   while(1) {
    swiWaitForVBlank();
   }
  }
  // Lee el archivo y ponlo en la RAM
  fread (BUFFER_TILES, 1, tiles_size, tiles_file);

 } else { // Archivo no encontrado
  iprintf("Archivo no encontrado.n");
  while(1) {
   swiWaitForVBlank();
  }
 }

 fclose(tiles_file);  // Cierra el archivo
 swiWaitForVBlank();  // Espera al cierre del archivo

 // Carga el archivo de MAP en la RAM
 long map_size = 0;  // Alamcenamos el tamaño del archivos de tiles
 FILE* map_file;  // Referencia al archivo de tiles
 sprintf(filename, "leccion01/fondo.map"); // Guarda el path + archivo
 map_file = fopen(filename, "rb");    // Y abre el archivo (modo lectura)

 if (map_file) {  // Si el archivo existe
  // Obten el tamaño del archivo
  fseek(map_file, 0, SEEK_END);
  map_size = ftell(map_file);
  rewind(map_file);
  // Reserva el espacio en RAM
  BUFFER_MAP = (char*) calloc (map_size, sizeof(char));
  if (BUFFER_MAP == NULL) {  // Si no hay suficiente RAM libre
   iprintf("RAM insuficiente.n");
   iprintf("Archivo: %ld bytes", map_size);
   while(1) {
    swiWaitForVBlank();
   }
  }
  // Lee el archivo y ponlo en la RAM
  fread (BUFFER_MAP, 1, map_size, map_file);

 } else { // Archivo no encontrado
  iprintf("Archivo no encontrado.n");
  while(1) {
   swiWaitForVBlank();
  }
 }

 fclose(map_file);  // Cierra el archivo
 swiWaitForVBlank();  // Espera al cierre del archivo

 // Carga el archivo de PAL en la RAM
 long pal_size = 0;  // Alamcenamos el tamaño del archivos de tiles
 FILE* pal_file;  // Referencia al archivo de tiles
 sprintf(filename, "leccion01/fondo.pal"); // Guarda el path + archivo
 pal_file = fopen(filename, "rb");    // Y abre el archivo (modo lectura)

 if (pal_file) {  // Si el archivo existe
  // Obten el tamaño del archivo
  fseek(pal_file, 0, SEEK_END);
  pal_size = ftell(pal_file);
  rewind(pal_file);
  // Reserva el espacio en RAM
  BUFFER_PAL = (char*) calloc (pal_size, sizeof(char));
  if (BUFFER_PAL == NULL) {  // Si no hay suficiente RAM libre
   iprintf("RAM insuficiente.n");
   iprintf("Archivo: %ld bytes", pal_size);
   while(1) {
    swiWaitForVBlank();
   }
  }
  // Lee el archivo y ponlo en la RAM
  fread (BUFFER_PAL, 1, pal_size, pal_file);

 } else { // Archivo no encontrado
  iprintf("Archivo no encontrado.n");
  while(1) {
   swiWaitForVBlank();
  }
 }

 fclose(pal_file);  // Cierra el archivo
 swiWaitForVBlank();  // Espera al cierre del archivo

 // Debug (Informa del tamaño de los archivos)
 iprintf("Tiles: %ld bytesn", tiles_size);
 iprintf("Map: %ld bytesn", map_size);
 iprintf("Pal: %ld bytesn", pal_size);
 iprintf("Carga ok.n");

 

 // Inicializa el motor 2D en "Modo 0" en la pantalla principal y habilita el fondo nº0
 videoSetMode(MODE_0_2D | DISPLAY_BG0_ACTIVE);

 // Define el banco de VRAM "A" para usarlo con el fondo
 vramSetBankA(VRAM_A_MAIN_BG);

 // Habilita el fondo nº0
 // BgType_Text8bpp indica que usaremos un fondo "Tileado" a 256 colores
 // BgSize_T_256x256 indica que el tamaño del fondo es de 256×256
 // Alamcena la ID exclusiva del fondo en "fondo"
 int fondo = bgInit(0, BgType_Text8bpp, BgSize_T_256x256, 0,1);

 // Usa el memcpy para copiar los datos de los Buffers en RAM a la VRAM
 // bgGetGfxPtr(fondo) – Obten el puntero donde almacenar los TILES en VRAM
 // bgGetMapPtr(fondo) – Obten el puntero donde almacenar el MAP en VRAM
 memcpy(bgGetGfxPtr(fondo), BUFFER_TILES, tiles_size);
 memcpy(bgGetMapPtr(fondo), BUFFER_MAP, map_size);
 memcpy(BG_PALETTE, BUFFER_PAL, pal_size);

 // Bucle (repite para siempre)
 while(1) {
 
  swiWaitForVBlank();  // Espera al sincronismo vertical
 
 }

 return 0;

}
 
He comentado el código lo suficiente como que sea auto explicativo (espero), de todas maneras si tenéis dudas, me dejáis un comentario en el blog.
La primera parte del código inicializa lo básico de la DS, interrupciones y demás. A continuación intentamos iniciar y acceder al sistema FAT de nuestra flashcard, de lograrlo, el programa continua. En caso de no poder ser, nos informa del error y detiene la ejecución del programa. A continuación declaramos e inicializamos 3 Buffers de memoria para poder almacenar en RAM los 3 archivos de los que se compone nuestro fondo. Acto seguido, procedemos a la carga de los archivos en si, esta parte de código, intenta abrir el archivo, devolviendo error si no se encuentra, obtiene el tamaño de dicho archivo y lo almacena en una variable, muy importante para más tarde poder dimensionar el buffer en RAM y su posterior copia en VRAM y finalmente, lee el archivo en el buffer correspondiente. He puesto un par de rutinas extras para verificar si ha sido correcta la carga del archivo en RAM y evitar disgustos.
La ultima parte simplemente inicializa el motor 2D de la DS y le dice que queremos iniciar una capa de fondo en la pantalla superior, usando la capa 0 (la más alta) con un fondo de 256×256 pixeles a 256 colores. Llegados a este punto, debéis saber que el hardware de la DS, sin hacer “trampas” solo soporta fondos desde 256×256 pixeles hasta 512×512 pixeles y sus variantes (512×256, 256×512). Para cargar fondos más grandes, ya tendremos de rascar código, pero eso será otro día).
 
El resultado del programa del ejemplo nos mostrara en la pantalla superior el fondo de muestra que hemos cargado y un texto de debug en la inferior, indicándonos el resultado de la inicialización del sistema FAT y el tamaño en bytes de los 3 archivos cargados.
Recordar en usar el MAKEFILE incluido en mi ejemplo, que ha sido modificado para usar las librerías FAT.
 
 
Uso de la utilidad GRIT.
 
Aquí aprenderemos a convertir un archivo BMP indexado a 256 colores a los 3 archivos que necesitaremos para nuestro ejemplo. La utilidad GRIT se ejecuta desde línea de comandos, así que para simplificar la cosa, he creado un archivo BAT que le pasa los parámetros más comunes para la conversión de los archivos BMP a TILES y de paso renombra los archivos generados y limpia los más necesarios. En la carpeta GRIT del ejemplo tenéis todo lo necesario.
Para convertir el archivo “fondo.bmp” a los 3 archivos necesarios, simplemente ejecutar desde línea de comandos:
 
convert fondo
 

Esto generara en la misma carpeta 3 archivos, “fondo.img”, que contiene los tiles, “fondo.map”, que contiene el mapa y “fondo.pal” que contiene la paleta.
Si queréis trastear vosotros con las posibilidades de esta utilidad, ejecutar la aplicación GRIT.EXE y veréis todos los parámetros disponibles. Mi BAT usa los siguientes:
 
grit.exe [archivo] -g -m -p -gB8 –ftb
 
Podéis también ver como está hecho el BAT simplemente abriendo el archivo “convert.bat”
 
 
Prueba del ejercicio.
 
Si el ejemplo ha compilado bien (debería si has usado el material adjunto a este tutorial), solo queda probarlo en la DS. Por algún bug en el emulador IDEAS, de momento no es posible probarlo en el emulador, así que tocara probarlo con la consola y el flashcard.
Crea una carpeta en la raíz de tu flashcard llamada “leccion01” y copia dentro el binario compilado (leccion01.nds) y parchéalo con DLDI si es necesario (en los flashcards mas reciente, no lo es) y los 3 archivos del fondo (fondo.img, fondo.map y fondo.pal).
Si todo es correcto, aparecerá el fondo en la pantalla superior (abre en tu PC el archivo fondo.bmp para compararlo) y en la pantalla inferior texto informando de la carga y tamaño de los archivos. Te animo a probar este ejemplo con tu propio fondo, el único punto a respetar es que sean BMP de 256×256 pixeles indexados a 256 colores.
 
 
Y esto es todo por hoy, espero que os haya gustado el tutorial, haya sido claro y de provecho para vosotros.
Aquí tenéis el enlace con todo el material necesario para esta lección
Proyecto en Visual C++:  
http://www.mediafire.com/?uctiqhmwfmd
GRIT con ejemplos:  http://www.mediafire.com/?nnrz4mrmdgz
Tambien podeis descargar el WORD de este tutorial aquí: http://www.mediafire.com/?tynmyyzouhy
 
Un cordial saludos a todos
 
NightFox

Tutorial de programacion de Nintendo DS. Dia 0. Instalacion del entorno.

Como prometi, aqui os dejo un minitutorial para introduciros (es decir, preparar el entorno) para empezar a trastear con nuestra DS.
Doy por sentado que nos defenderemos en la instalacion de entornos de programacion y tenemos nociones de programacion (C). Si teneis algua duda, a los comentarios.

— Nota — 28 de Abril del 2010 —
Este tutorial esta bastante desfasado.
Podeis ver la nueva version actualizada en la siguiente web de nuestro grupo:
 
Tutorial "Primeros pasos" en la web oficial de NightFox & Co.
 
 
 
Tutorial de programación
Dia 0: Instalacion del entorno.
 
Para empezar estas minilecciones de programación para Nintendo DS necesitaremos instalarnos el siguiente software de desarrollo:
1. Visual C++ Express 2005 con sus service packs
2. devKitARM, en su versión R25 (la ultima a fecha de hoy)
 
Lo primero será descargarnos de la web de Microsoft y de manera gratuita el Visual C++.
Aquí teneis los enlaces:
Visual C++ 2005 Express:
http://www.microsoft.com/express/2005/download/default.aspx#
La instalación de esta aplicación se realiza on-line, asi que tardara un poco.
Service Pack  1:
http://download.microsoft.com/download/7/7/3/7737290f-98e8-45bf-9075-85cc6ae34bf1/vs80sp1-kb926748-x86-intl.exe
Parche para Windows vista: http://www.microsoft.com/downloads/details.aspx?familyid=90e2942d-3ad1-4873-a2ee-4acc0aace5b6
 
Yo lo he instalado todo en “C:NDS”, asi que en mi caso el Visual C++ ha quedado instalado en “C:NDSMicrosoft Visual Studio 8”.
 
Lo siguiente es descargar e instalar el devKitARM r25.
Lo podemos obtener del siguiente enlace,
devKitARM:
http://sourceforge.net/project/showfiles.php?group_id=114505&package_id=16039
Instalaremos todo lo referente a devKitARM, incluyendo los ejemplos de NDS, los Kits para PSP y Game Cube, asi como los ejemplos de GBA y GP32 los podemos omitir.
Una cosa importante, la ruta donde instalemos el devKitArm no puede contener ningún espacio en blanco, en mi caso he usado “C:NDSdevKitPro”.
 
Lo siguiente será descargarnos una pequeña utilidad que preconfigurara nuestro VC++ para poder compilar proyectos para NDS.
He modificado el Wizard de las PALIB para poder usarlo con las libnds (asi es como se llaman las librerías incluidas en el devKitARM), asi que aunque aparezca como si fuera a configurarse todo para usar las PALIB, no es asi.
Esta utilidad la podréis descargar de aquí:
http://www.mediafire.com/?mczyzd00rzh
Descomprimimos el contenido del zip en una carpeta y ejecutamos el archivo “VC8_Express_Setup.js” . Si todo ha ido bien, aparecerá un mensaje de confirmación.
 
Ahora ya podemos abrir el Visual C++ Express 2005, para realizar una configuración rápida del mismo.  Iremos al menú “TOOLS”, escogeremos “OPTIONS” y desplegaremos el menú “Projects and solutions”, marcando la opción “General”. En el apartado “Visual Studio Projects locations” escribiremos la ruta donde deseemos guardar nuestros proyectos, en mi caso “C:NDSProjects” (ver imagen aquí
http://www.mediafire.com/?nkemfqymedj) y recordad, no useis ningún carácter raro ni espacio en blanco en esta ruta, de hacerlo, posiblemente no compile.
Hecho esto, ya podemos crear nuestro primer proyecto (incluido en la plantilla) y testear si el entorno se ha instalado correctamente.
Para ello iremos al menú “File”, “New” y “Project” y seleccionaremos “Nintendo DS” como opción. En la casilla escribiremos “Hello” como nombre de proyecto (recordad, ningún espacio en blanco), le daremos a “Ok” y completaremos el asistente dándole a “Next” y a “Ok” dejando lo demás por defecto. (Ver imagen
http://www.mediafire.com/?thmy1jjldme) Si todo ha salido como debería y veremos esto http://www.mediafire.com/?xmjmwmjmodn o algo muy parecido es que vamos bien.
En este punto ya podemos compilar el proyecto (el ultrahipermegafamoso Hola Mundo!) y verificar si todo esta como debería.
Para ello, le daremos con el botón derecho a “Solution Hello (1 project)” (arriba a la izquierda) y seleccionaremos “Clean Solution”.
En la ventana output veremos esto:
 
1>—— Clean started: Project: Hello, Configuration: Release Win32 ——1>Performing Makefile project actions1>clean …========== Clean: 1 succeeded, 0 failed, 0 skipped ==========
 
Lo cual nos indica que el commando “Clean” (es decir, borrar todo lo compilado anteriormente) se ha ejecutado con exito.
Ahora pasaremos a la compilación en si. Para ello, haremos lo mismo que en el paso anterior, pero seleccionaremos la opción “Build Solution”. Si todo ha salido bien (debería si has seguido este tutorial al pie de la letra), la ventana output mostrara lo siguiente:
 
1>—— Build started: Project: Hello, Configuration: Release Win32 ——1>Performing Makefile project actions1>main.cpp1>arm-eabi-g++ -MMD -MP -MF /c/NDS/projects/Hello/Hello/build/main.d -g -Wall -O2 -march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math -mthumb -mthumb-interwork -I/c/NDS/projects/Hello/Hello/include -I/c/NDS/projects/Hello/Hello/build -I/c/nds/devkitPro/libnds/include -I/c/nds/devkitPro/libnds/include -I/c/NDS/projects/Hello/Hello/build -DARM9 -fno-rtti -fno-exceptions -c /c/NDS/projects/Hello/Hello/source/main.cpp -o main.o1>linking Hello.elf1>built … Hello.arm91>ndstool -c /c/NDS/projects/Hello/Hello/Hello.nds -9 /c/NDS/projects/Hello/Hello/Hello.arm9 -b /c/nds/devkitPro/libnds/icon.bmp "Hello;www.devkitpro.org;www.drunkencoders.com" 1>Nintendo DS rom tool 1.40 – Feb  4 20091>by Rafael Vuijk, Dave Murphy, Alexei Karpenko1>built … Hello.nds1>Build log was saved at "file://c:NDSprojectsHelloHelloReleaseBuildLog.htm"1>Hello – 0 error(s), 0 warning(s)========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

Si  vemos esto, premio. En la carpeta “C:ndshellohello” (si has usado las carpetas que he mencionado) tendras el archivo “hello.nds”. Ahora solo queda que lo pruebes en tu Nintendo DS (usando un flashcard) o en el emulador de turno (recomendable por temas de depuración).
Si la cosa ha salido bien, veras esto:
http://www.mediafire.com/?mmrug2nmtmj
 
Pues aquí termina el primer dia. A medida que domine las LIBNDS, os pondré mas tutoriales.
 
Aqui podeis descargaros ente tutorial en formato WORD: http://www.mediafire.com/?jmznrwmtnwt
 
Un saludo

 

NightFox

Proximamente: Tutorial de programacion

Buenas!
Vale, hace dias que no dava señales de vida. Los culpables, (a parte de el curro) son Guitar Hero y Dead Space (que cosas, tambien juego) y basicamente que cada dia me he amargado mas de las "limitaciones" (obias) de las PALIB (un año y medio sin actualizarse).
Asi que dado que me va a tocar aprender a programar a pelo para DS (vamos LIBNDS) a medida que aprenda ire publicando minitutoriales de programacion, para asi a medida que yo descubra a hacer las cosas con libnds, poderlas compartir con vosotros.
En breve, tutorial para instalar el VC++ Express 2005 (oye, este seguro que funciona), instalacion del devkitarm R25 y el primer "Hello world" usando esto.

La cosa es que eso, de saber hacerlo, lo se hacer, pero tengo que mirar como os hago el tutorial.
Pero lo tendreis en breve.
 
Hasta entonces un abrazo a todos.

Fox