Ayuda Chocobo Dungeon 2 [Fuente]

Lugar donde se intentarán resolver problemas específicos
Avatar de Usuario
CUE
Administrador
Administrador
Mensajes: 5601
Registrado: 24 Ene 2011, 16:52

Mensaje por CUE » 15 Jun 2011, 19:44

Primero, y con el único propósito de ocupar espacio para que parezca que estamos haciendo algo de la leche y no sólo las 4 líneas necesarias, hacemos unas deficiniones.

Sabemos el nombre del fichero y dónde comienzan los datos de la fuente:

Código: Seleccionar todo

#define FILENAME "ALLBIN.BIN"
#define POSITION 0xA40000
El número de caracteres de la fuente, así como la altura y anchura de los mismos se debería sacar de los datos, pero como son siempre los mismos nos lo saltamos y lo hacemos a pelo:

Código: Seleccionar todo

#define NCHARS   0x017A
#define HEIGHT   0x0C
#define WIDTH    0x0C
Sabemos también que hay una cabecera de 72 bytes, correspondientes a 4 bytes, 2 paletas de 16 colores de 16 bits y 4 bytes con el número de caracteres. También sabemos que cada carácter va precedido de 12 bytes con información para situarlo en la VRAM:

Código: Seleccionar todo

#define OFFSET   72 // 4 + 2*(16*2) + 4
#define SKIP     12
Ahora vamos a definir tamaños. Hacer lecturas byte a byte como haces es muy lento, y, como cualquier fuente no es muy grande, vamos a trabajar con la memoria, así que cargaremos todo en la misma. Como cada carácter lleva 2 pixeles sabemos que los datos reales del carácter serán HEIGHT*WIDTH/2, a los que hay que sumar los 12 bytes de información. Eso para cada carácter. Además hay que sumar los 72 bytes iniciales. Con eso ya tenemos el tamaño real que ocupa la fuente en el fichero:

Código: Seleccionar todo

#define FILESIZE (NCHARS * (SKIP + HEIGHT * WIDTH / 2) + OFFSET)
Cada una de las semifuentes se forma cogiendo 6 bytes originales, en grupos de 2, que nos da 3 bytes para cada una de las fuentes. Como queremos que el número de pixeles sea múltiplo de 8 para poder verlo con el Tile Molester, eso quiere decir que debemos añadir 4 pixeles, justo los que caben en un byte. O sea, que hay que añadir un byte extra a esos 3. Así que ya sabemos que por cada 6 bytes de datos debemos generar 4, por lo que el tamaño de cada semifuente será (HEIGHT*WIDTH/2)*4/6:

Código: Seleccionar todo

#define FONTSIZE (NCHARS * HEIGHT * WIDTH / 3) // H*W/2 * 4/6
Y después de todo este rollo, que podíamos habernos saltado, pero así queda mucho más 'potito', vamos a lo nuestro.

Lo primero es crearnos 3 tablas en memoria. Como en este caso son pequeñas, pasamos de hacerlas dinámicas y así evitamos tener que crearlas y liberarlas al final. Las 3 serán de tipo char (caracteres, bytes o como quiera cada uno llamarlas):

Código: Seleccionar todo

char buffer[FILESIZE], font1[FONTSIZE], font2[FONTSIZE];
Ahora vamos a leer los datos en memoria. En los lenguajes .NET creo recordar que para posicionar un fichero se usaba la instrucción Position.Set, que es lo que te faltaba a ti, pero yo paso de NETs y lo hago de la forma clásica, que ocupa mucho menos, y que después cada uno lo haga a su manera:

Código: Seleccionar todo

FILE *fp = fopen(FILENAME, "rb");
fseek(fp, POSITION, SEEK_SET);
fread(buffer, sizeof(char), FILESIZE, fp);
fclose(fp);
Ya está. Con 4 míseras líneas ya tenemos los datos originales en memoria y no nos hemos despeinado.

Ahora, y para usar C de verdad, vamos a utilizar punteros para apuntar a cada una de las tablas:

Código: Seleccionar todo

char *pb = buffer + OFFSET; // origen de los datos
char *p1 = font1; // posición 0 de la fuente 1
char *p2 = font2; // posición 0 de la fuente 2
Y ya sólo nos queda el proceso, que es el mismo para carácter, así que a usar unos bucles, como mandan los cánones. Cada carácter tiene unas filas, y cada fila un ancho. Ya sabemos que antes de tomar los datos hay que saltar los bytes iniciales, y que hay 2 pixeles por cada byte, siendo necesarios 3 grupos de 2 bytes para completar cada fila (WIDTH/4), y al final siempre hay que añadir un byte más:

Código: Seleccionar todo

for (int c = NCHARS; c; c--) {
  pb += SKIP;
  for (int h = HEIGHT; h; h--) {
    for (int w = WIDTH / 4; w; w--) {
      // aquí va el meollo
    }
    *p1++ = *p2++ = 0xAA; // color 10.10.10.10 en binario
  }
}
NOTA: En vez de añadir un cero patatero, añado 0xAA para que se distinga mejor el carácter resultante. En realidad debería ser 0x55, pues 01 es el color usado para la transparencia, pero a mí me gusta más así.

Ahora vamos a ver cómo separar los datos. Ya hemos visto que partiendo de 6 bytes obtenemos dos series de 3 bytes:

Código: Seleccionar todo

buffer |  abABcdCD efEFghGH  |  ijIJklKL mnMNopOP  |  qrQRstST uvUVwxWX  |
       | abAB-cdCD efEF-ghGH | ijIJ-klKL mnMN-opOP | qrQR-stST uvUV-wxWX |
       | cdCD-abAB ghGH-efEF | klKL-ijIJ opOP-mnMN | stST-qrQR wxWX-uvUV |
font1  |     cd-ab-gh-ef     |     kl-ij-op-mn     |     st-qr-wx-uv     |
font2  |     CD-AB-GH-EF     |     KL-IJ-OP-MN     |     ST-QR-WX-UV     |
Vamos a hacerlo para los dos primeros bytes y el resto es igual. Con 'abABcdCD' y 'efEFghGH' sacamos 'cdabghef' para la fuente 1 y 'CDABGHEF' para la fuente 2. No hay que olvidar que inicialmente tenemos 2 valores de 4bpp y de cada uno de ellos salen 2 valores de 2bpp, y hay que tener en cuenta el 'reverse', que ya está explicado en un post anterior:

Código: Seleccionar todo

V1 = abABcdCD
V2 = efEFghGH

pixel ab: ab------ = V1 & 0xC0 (11000000 en binario)
pixel AB: --AB---- = V1 & 0x30 (00110000 en binario)
pixel cd: ----cd-- = V1 & 0x0C (00001100 en binario)
pixel CD: ------CD = V1 & 0x03 (00000011 en binario)
pixel ef: ef------ = V2 & 0xC0 (11000000 en binario)
pixel EF: --EF---- = V2 & 0x30 (00110000 en binario)
pixel gh: ----gh-- = V2 & 0x0C (00001100 en binario)
pixel GH: ------GH = V2 & 0x03 (00000011 en binario)
Ahora hay que pasar esos valores a los nuevos de la fuente, teniendo en cuenta que tenemos que desplazar algunos de ellos varios bits. Por ejemplo, 'ab' debemos deplazarlo 2 bits a la derecha para situarlo en su posición final, pues originalmente ocupa los bits 7-6 y debemos ponerlo en los bits 5-4:

Código: Seleccionar todo

Para fuente 1&#58; &#40;V1 & 0x30&#41; | &#40;&#40;V1 & 0x03&#41; << 6&#41; | &#40;&#40;V2 & 0x30&#41; >> 4&#41; | &#40;&#40;V2 & 0x03&#41; << 2&#41;
Para fuente 2&#58; &#40;&#40;V1 & 0xC0&#41; >> 2&#41; | &#40;&#40;V1 & 0x0C&#41; << 4&#41; | &#40;&#40;V2 & 0xC0&#41; >> 6&#41; | &#40;V2 & 0x0C&#41;
Una vez hecho todo eso, ya sólo queda grabar las 2 fuentes, cada una en un fichero:

Código: Seleccionar todo

fp = fopen&#40;"fnt1.raw", "wb"&#41;; fwrite&#40;font1, sizeof&#40;char&#41;, FONTSIZE, fp&#41;; fclose&#40;fp&#41;;
fp = fopen&#40;"fnt2.raw", "wb"&#41;; fwrite&#40;font2, sizeof&#40;char&#41;, FONTSIZE, fp&#41;; fclose&#40;fp&#41;;
Y no hay mayor misterio.

Ahora sólo queda modificar los dos ficheros resultantes y hacer el proceso inverso, es decir, coger un byte de cada fuente para regenerar los dos byte iniciales. Pero eso es otra historia que debe ser contada en otra ocasión, aunque no por mí.

Todo junto, para que se vea a simple vista lo poco que ocupa, listo para compilar con cualquier compilador de C clásico:

Código: Seleccionar todo

#include <stdio.h>

#define FILENAME "ALLBIN.BIN"
#define POSITION 0xA40000
#define OFFSET   72 // 4 + 2*&#40;16*2&#41; + 4
#define NCHARS   0x017A
#define HEIGHT   0x0C
#define WIDTH    0x0C
#define SKIP     12
#define FILESIZE &#40;NCHARS * &#40;SKIP + HEIGHT * WIDTH / 2&#41; + OFFSET&#41;
#define FONTSIZE &#40;NCHARS * HEIGHT * WIDTH / 3&#41; // H*W/2 * 4/6

int main&#40;void&#41; &#123;
  char buffer&#91;FILESIZE&#93;, font1&#91;FONTSIZE&#93;, font2&#91;FONTSIZE&#93;;

  FILE *fp = fopen&#40;FILENAME, "rb"&#41;;
  fseek&#40;fp, POSITION, SEEK_SET&#41;;
  fread&#40;buffer, sizeof&#40;char&#41;, FILESIZE, fp&#41;;
  fclose&#40;fp&#41;;

  char *pb = buffer + OFFSET; // origen de los datos
  char *p1 = font1; // posición 0 de la fuente 1
  char *p2 = font2; // posición 0 de la fuente 2

  for &#40;int c = NCHARS; c; c--&#41; &#123;
    pb += SKIP;
    for &#40;int h = HEIGHT; h; h--&#41; &#123;
      for &#40;int w = WIDTH / 4; w; w--&#41; &#123;
        char ch1 = *pb++;
        char ch2 = *pb++;
        *p1++ = &#40;ch1 & 0x30&#41; | &#40;&#40;ch1 & 0x03&#41; << 6&#41; | &#40;&#40;ch2 & 0x30&#41; >> 4&#41; | &#40;&#40;ch2 & 0x03&#41; << 2&#41;;
        *p2++ = &#40;&#40;ch1 & 0xC0&#41; >> 2&#41; | &#40;&#40;ch1 & 0x0C&#41; << 4&#41; | &#40;&#40;ch2 & 0xC0&#41; >> 6&#41; | &#40;ch2 & 0x0C&#41;;
      &#125;
      *p1++ = *p2++ = 0xAA;
    &#125;
  &#125;

  fp = fopen&#40;"fnt1.raw", "wb"&#41;; fwrite&#40;font1, sizeof&#40;char&#41;, FONTSIZE, fp&#41;; fclose&#40;fp&#41;;
  fp = fopen&#40;"fnt2.raw", "wb"&#41;; fwrite&#40;font2, sizeof&#40;char&#41;, FONTSIZE, fp&#41;; fclose&#40;fp&#41;;

  return&#40;0&#41;;
&#125;
(ahora quitamos los 'defines' y ponemos los valores directamente y se nos queda en nada)

Avatar de Usuario
tchusami
Mensajes: 86
Registrado: 29 Ene 2011, 20:01
Ubicación: Librilla
Contactar:

Mensaje por tchusami » 15 Jun 2011, 21:55

impresionante!! :shock: ojala nos enseñaran a hacer todas estas cosas... no estamos nada mas que con TDAs uff...

Avatar de Usuario
CUE
Administrador
Administrador
Mensajes: 5601
Registrado: 24 Ene 2011, 16:52

Mensaje por CUE » 17 Jun 2011, 09:29

... ojala nos enseñaran a hacer todas estas cosas...
... se entiende todo bastante bien...
... yo aprendí a manejar los ficheros un poco distinto, pero totalmente adaptable a como lo ha hecho CUE.
El problema es que hoy día enseñan las cosas de una forma totalmente dependiente del compilador usado y del sistema operativo, sin tener en cuenta nada más. ¿Por qué es así? Pues porque nadie te va a enseñar a programar, lo único que hacen, ya sea en la uni, en una academia, unos manuales, etc., es enseñarte a poner unas instrucciones que se pueden sacar de cualquier libro. Y si es en casos como éste, donde tienes que aplicar cosas que no tienes por qué saber (fuentes solapadas, gráficos 'reverse', 4bpp, 2bpp), pues no hay que contar con que las enseñanzas de ningún profesor nos saquen las castañas del fuego.

Por ejemplo, en este caso hay 2 cosas que no interesan demasiado (bueno, sí, pero no para cómo queremos hacer el proceso):
- las 3 tablas de las fuentes: da igual que se creen de forma estática, como he hecho yo, o de forma dinámica, donde cada uno usará lo que sabe (operador 'new', función 'malloc', etc.), lo único que importa es que sean tablas de caracteres, se llamen bytes, char o como sea
- el tratamiento de ficheros: no importa cómo se lea ni qué funciones se usen, eso es algo totalmente separado del proceso que queremos hacer, aunque eso sí, no es recomendable que leyendo/grabando byte a byte se empleen casi 60.000 accesos al disco duro cuando se puede hacer con 3 (una lectura y dos escrituras)

Al final debe quedar algo como lo puesto: una inicialización, que es lo que va a depender de lo que uses, ya sea C, C++, C# o el primo de C-mosol que se use. Después deberá ir el proceso, totalmente independiente de las funciones propias del compilador que uses y que deberá servir en cualquier situación, independientemente de lo que uses para programar (hablando de C's claro). Al final, al grabar los datos, ya dependerá otra vez de lo que estés usando.

Resumiendo: se crean las tablas como se quiera, se graban como se quiera, pero el proceso a usar para generar las fuentes deberá ser literalmente como lo he puesto, sin tener que tocarlo, a no ser que las tablas se hagan de otra forma, claro.

Claro que aquí también influye la forma de hacer las cosas de cada uno. Yo estoy acostumbrado a hacer los análisis sin saber quién va a programar ni qué lenguaje se va a usar, así que tengo que poner ejemplos que me sirvan en la mayoría de los casos. Y, como en este caso, me basta el maravilloso, y a veces olvidado, bloc de notas del windows para crear los programas, sin necesidad de ningún mega-compilador-chachi-piruli para hacer las cosas.

Y nada más por hoy, que, curiosamente, tengo que ir a explicar cómo hacer unas cosas para que el código sirva para windows y linux.

Avatar de Usuario
gadesx
Administrador
Administrador
Mensajes: 2006
Registrado: 24 Ene 2011, 16:43
Ubicación: El puche
Contactar:

Mensaje por gadesx » 28 Jul 2011, 19:33

Y toda esa historia de arriba puede valer para el 1?

Código: Seleccionar todo

Chocobo Collection - Happy 10th Anniversary! - Dice de Chocobo &#91;Disc3of3&#93; &#91;J&#93; &#91;SLPS-02523&#93;	
Chocobo no Fushigi Dungeon &#91;Disc1of2&#93; &#91;J&#93; &#91;SLPS-01234&#93;							
Chocobo no Fushigi Dungeon &#91;Disc2of2&#93; &#91;J&#93; &#91;SLPS-01235&#93;							
Chocobo no Fushigi Dungeon 2 &#91;Disc1of2&#93; &#91;J&#93; &#91;SLPS-01771&#93;						
Chocobo no Fushigi Dungeon 2 &#91;Disc2of2&#93; &#91;J&#93; &#91;SLPS-01772&#93;	

Responder