[NDS] Problemas con GCC y libfat

Buenas!

Primera vez que posteo por aqui, a ver si me podeis ayudar.

Tengo un fichero binario del que quiero leer una estructura (el fichero en si es una sucesion de la misma estructura) que es asi:

#pragma pack(push, 1)
struct MulCell
{
u16 TileID;
s8 Z;
};

struct MulBlock
{
MulCell cells[8][8];
};
#pragma pack


Entonces, el problema viene de que GCC (el arm-eabi-g++) no soporta ese tipo de #pragmas, y el substituto que me han recomendado __atribute__((__packed__)) da un warning de compilacion diciendo que ese atributo sera ignorado..., asi que al leer el archivo me da datos corruptos, la unica solucion que he encontrado y que no me gusta un pelo es convirtiendo el archivo para que las dos variables de la estructura sean del mismo tipo, en este caso u16, entonces lee los datos bien... pero eso, no me gusta un pelo, porque ahora con este archivo aun puedo vivir asi aunque aumente el tamaño un poco, pero en otros... nanai, imposible.

Teneis alguna sugerencia/idea?

Gracias desde ya!

Por cierto, en win32 el mismo codigo de lectura funciona a la perfeccion, dando los datos que tiene que dar...

(El post original esta en: http://forum.gbadev.org/viewtopic.php?t=11708 )
Bueno, tu problema es muy sencillo.

Por un lado, parece que el ARM tiene forzado el tamaño minimo de dato de la estructura a 4, por lo que el atributo packed es ignorado.

y por otro, estas utilizando la formula push/pop , que permite guardar la alineacion y luego recuperarla, que no esta soportado para ARM9

¿solucion? Muy facil:

#pragma pack(1) // alineacion a 1

struct MulCell
{
u16 TileID;
s8 Z;
};

struct MulBlock
{
MulCell cells[8][8];
};

#pragma pack(0) // restaura la alineacion por defecto (a 4)
Si, eso lo he probado y nada, sigue dando los mismos valores incorrectos.

Lei en el foro de gbadev que en vez de utilizar desde windows el fwrite para crear el archivo y el fread para leerlo en nds, que lo haga con fprintf y fscanf... voy a probar.

Edit:

He seguido probando cosas y ninguna de ellas funciona, he probado a leer el archivo desde win32 y crearlo de nuevo con otro nombre pero esta vez con un fprintf en vez del fread (segun dijeron por el foro de gbadev, el tema de fwrite/fread en distintas plataformas puede dar problemas), asi que lo probe asi y a leer desde la rom con un fscanf, pero da datos mas extraños aun... aunque esto puede ser perfectamente error mio :P, en fin, que ando un poco a la desesperada, porque uno de los pilares fundamentales del proyecto es tener acceso a estos archivos e ir leyendo cuando haga falta (porque meter un mapa de 80 megas en memoria... dificil xD).
En fin, que ya no se que hacer, el primer bloque de (64 MulCells) lo lee bien, da los datos correctos, pero a partir de ahi, supongo que por la alineacion de bytes (o la fase lunar, ya no se que pensar) los datos los da totalmente incorrectos... asi que, si alguien me pudiese ayudar le estaria eternamente agradecido.
CthulhuRlz escribió:Si, eso lo he probado y nada, sigue dando los mismos valores incorrectos.

Lei en el foro de gbadev que en vez de utilizar desde windows el fwrite para crear el archivo y el fread para leerlo en nds, que lo haga con fprintf y fscanf... voy a probar.



Humm, fwrite y fread... ¿no estaras usandolos mal?

Me explico: en esas funciones, puedes especificar el tamaño de un elemento y el numero de elementos a leer, asi que podria ser que aunque la alineacion dentro de la estructura se haga correctamente (que lo hace), el tamaño de la estructura si que resulte alineado (y sea un byte mayor, por ejemplo).

Especifica el tamaño a mano, sin utilizar sizeof y me cuentas. Tambien procura que el tamaño del dato sea 1 y el numero de elementos a leer, se corresponda con el numero de bytes a leer, que quiza te solucione algun problema relacionado con las alineaciones de buffer interna a esas dos funciones.


Ya que estamos, deberias tener en cuenta un detalle: siempre que accedas a un elemento de la estructura, como dato, el compilador generará codigo adicional para poder leer el dato desalineado (el ARM no puede leer un int directamente si esta desalineado a 4 o un short que no esté alineado a 2 , ya sabes)

El problema viene si tratas de acceder a ese dato con la ayuda de un puntero, por ejemplo, u16 *punt; y asignarle un elemento individual.

Mi recomendacion seria de que siempre que puedas trates de ordenar los datos de una estructura de forma que la alineacion a 4 se mantenga y con eso evitaras problemas.

Utilizar el pragma te da la posibilidad de acceder a los datos con ciertas restricciones y ademas, te va a penalizar en velocidad, asi que no parece nada interesante utilizar estructuras desalineadas, salvo que no haya otra solucion posible.
Hmmm!

La estructura esta alineada a 3 (unsigned short = 2, signed char = 1), por lo que quedaria un byte suelto por ahi, lo que no entiendo es porque los primeros 64 grupos los lee bien y el resto no.
En fin, aqui posteo el codigo que utilizo para leer, espero que entre todos podamos sacar algo en claro de todo esto.
void UOMap::getBlock(u16 x, u16 y, MulBlock *block)
{
x %= width;
y %= height;

u32 blockID = (x / 8) * (height / 8) + (y / 8);
u32 offset = blockID * 196;

fseek(fmap, offset, SEEK_SET);
unsigned int header;
fread(&header,4, 1, fmap);
//int h = fread(&block->cells, 3, 64, fmap); <---- metodo alternativo pero que da los mismos resultados.

for(int i = 0; i < 8; i++)
{
for(int j = 0; j < 8; j++)
{
fread(&block->cells[i][j].TileID, 2, 1, fmap);
fread(&block->cells[i][j].Z, 1, 1, fmap);
}
}
}




Edit:

Mi recomendacion seria de que siempre que puedas trates de ordenar los datos de una estructura de forma que la alineacion a 4 se mantenga y con eso evitaras problemas.


Entonces, deberia añadir a la estructura un char "basura" para que este alineada a 4? En este archivo me da igual cambiarlo y añadir/suprimir cosas, pero en los otros...

Utilizar el pragma te da la posibilidad de acceder a los datos con ciertas restricciones y ademas, te va a penalizar en velocidad, asi que no parece nada interesante utilizar estructuras desalineadas, salvo que no haya otra solucion posible.

Es que todos estos archivos son de un juego ya existente, estaba haciendo un parser en Windows para eliminar datos no necesarios, pero ahora me entra la duda... tendre que mirarme el formato de todos los archivos para ver alineaciones, algo en lo que nunca habia pensado... en menudo infierno me he metido xDDD

Edit 2:
Que emulador es el mas apropiado para esto? Que yo sepa el unico que tiene soporte para FAT es el Dualis, pero no para la ultima version de la libfat (que quiza todo se deba a que no la utilizo y mi codigo este bien...) que directamente abre y cierra la rom, de momento no puedo probar esto en una DS hasta que recupere la mia este jueves (novias... xD).
CthulhuRlz escribió:Hmmm!

La estructura esta alineada a 3 (unsigned short = 2, signed char = 1), por lo que quedaria un byte suelto por ahi, lo que no entiendo es porque los primeros 64 grupos los lee bien y el resto no.
En fin, aqui posteo el codigo que utilizo para leer, espero que entre todos podamos sacar algo en claro de todo esto.




Edit:



Entonces, deberia añadir a la estructura un char "basura" para que este alineada a 4? En este archivo me da igual cambiarlo y añadir/suprimir cosas, pero en los otros...



Mira, para que resuelvas tus dudas, haz lo siguiente, imprime el sizeof de cada estructura en pantalla, y asi sabras si el compilador te está alineando en tamaño.

De todas formas, si quieres podría analizar el codigo que has posteado mañana, por si hay algun fallo del compilador que toca los cojones (hoy no estoy de 'humor' para programar o darle demasiado al coco), pero no te preocupes , que eso lo vamos a mirar al detalle, pues a mi me interesa tambien.


Ayer como yo ya me he tenido que pelear con estos problemas en multiples versiones de compiladores y maquinas, hice un codigo de ejemplo para comprobar que el #pragma pack(1) funcionaba correctamente e imprimia las direcciones y el dato de forma correcta, todo eso desde una simple estructura.

Eso si, tu estas utilizando un array y todo va a depender de como haga el compilador para interpretar los datos: si tienes un elemento alineado a 3, es posible que el te permita leer o escribir cda uno de los elementos de forma desalineada, pero a la hora de tratarlo como array, te alinee el tamaño a 4.

Lo que no se, es que tal te iria aplicar el attribute packed una vez que ya tienes definida la alineacion a 1 con pragma. Lo mismo se lo traga y te adapta el tamaño de la estructura para que coincida con el tamaño real de los datos y resuelves el problema, o lo mismo no consigues nada XD.

En fin, mañana en un rato lo puedo mirar, pero hoy no es el dia adecuado (voy a ver si le doy un poco al GOW, jeje)

EDITADO:

Yo creo que el problema es mas bien de formacion de estructura, que de las lecturas que estas usando (salvo que se a una corrupcion de datos raro). La estructura se utiliza bien, elemento a elemento, pero el tamaño final puede que se alinee a 4,lo que explicaria esos problemas que tienes. Mañana lo miro con mas detalle, a ver como se puede solucionar ;)
Muchisimas gracias!
Si necesitas el archivo que estoy leyendo y todo el source del proyecto, solo tienes que pedirlo y lo subire a algun sitio :)

EDIT:

Llevo un buen rato mirando con un editor hexadecimal el archivo fuente del que tengo que leer la informacion... y todo esta correcto, todo parece estar en su sitio, he ido mirando posiciones aleatorias, calculando el offset desde el programa hecho en windows y haciendo fseeks para coger la informacion y tanto en windows como en el editor hexadecimal (haciendo un go to al offset calculado) me daba todo correcto. Asi que o se trata de algo del ARM o de la libfat (que no utilizo la ultima porque no funciona en emuladores... voy con la libfat-20060709). Hasta que no tenga la DS en mis manos (este jueves) no podre hacer tests con la ultima version de la libfat y descartar eso.
Me fastidiaria bastante tener que descartar el soporte FAT del juego y la verdad, no se como funcionan los otros FS ni si me daran los mismos problemas que esto, asi que... ya no se que hacer :(
Hola.

Perdona el retraso, pero el sabado me apetecia terminar el GOW y ayer cuando me puse, estuve trabajando en el nuevo Wifiloader y no me he puesto hasta ahora a mirar lo tuyo.

Te cuento: segun mis pruebas, el compilador funciona perfectamente, incluso me funciona bien creando la estructura despues del pack(0) e incluso si lo fuerzo a pack(4)


Veamos, si creo la estructura asi:

#pragma pack(1) // alineacion a 1

struct MulCell
{
u16 TileID;
s8 Z;
};

struct MulBlock
{
MulCell cells[8][8];
};

#pragma pack(0) // restaura la alineacion por defecto (a 4)

#pragma pack(4)
struct MulBlock prueba ={5,8,9,10};


Incluso si 'prueba' lo defino como local, me pilla bien los valores e imprimiendo las direcciones de cada elemento (TileID y Z) las direcciones son completamente correctas (y el tamaño de las estructuras, tambien es correcto)

Es decir: que no se puede hablar de fallo de compilador, a no ser que estes usando una version de GCC diferente (yo uso el 4.1.1 de devkitarm release 19b, cosa que puedes ver si metes en consola arm-eabi-gcc --version ). Salvo que estes usando una optimizacion con la que se atragante el compilador, solo se me ocurre una cosa.

Veamos, tu problema viene de usar la libfat, con gran probabilidad. En mi opinion, es probable que libfat requiera que le pases un buffer alineado, asi que haz lo siguiente:

- Crea un buffer temporal que este alineado a 4 (la forma facil es usar un array de ints o unsigned)

- Lee los datos que necesites ahi y luego los copias a tu estructura con ayuda de memcpy

No he mirado el fuente de libfat, pero es muy probable que esté utilizando alguna dma o rutina de copiado rapido que provoque ese fallo si el buffer esta desalineado. O eso o hay algun fallo en la libreria de algun tipo, pues ya te digo que desde mi ejemplo de prueba se comporta a la perfeccion, incluso me mantiene la alineacion de base cuando creo la estructura en un punto donde el pragma pack es diferente.

Si te sigue fallando con eso, prueba a leer todo el fichero en un buffer alineado a 4 y copia los datos con ayuda de memcpy. Si eso no te funciona, sera mejor que pases de libfat :P
Hola!

Gracias por responder! He estado probando eso y, o es cosa del emulador o de la version de la libfat que tengo yo (recordemos que la ultima libfat no funciona en emuladores), pero me sigue pasando exactamente lo mismo. Este jueves al fin podre recuperar mi DS (estas novias... xD) y lo probare por fin en hardware.
Hace unos minutos que he estado probando alternativas, de momento el KOSfs me da medianamente buenos resultados, me hace bien los fseeks, no me lee basura y me coge bien la informacion de TileID, pero el valor de Z se lo inventa..., no tengo ni idea de lo que lee (ejemplo, tendria que leerme un -46 y me devuelve un 210...), dudo que sea de alineacion, porque sino al leer el siguiente valor, que es otro TileID de la siguiente estructura, me tendria que leer basura y no, me da el valor correcto, no se si sera por la historia de que estoy probando esto en emulador, de la fase lunar o de que hace mucho frio en mi casa..., de todas formas intentare escribir un mail al que hizo el port, para ver si es cosa mia o de el.

La verdad es que me voy desmoralizando poco a poco, fui avanzando bastante bien hasta el momento de meter los FS, y parece que con esta "trivialidad" me voy a quedar encallado de por vida. En fin, espero que en cuanto recupere mi DS todo vuelva a funcionar bien.
Los emuladores estan bien para probar ciertas cosas, pero no para todo ¿Por que te crees que he trabajado en Wifiloader como utilidad prioritaria? Necesitaba algo que me permitera cargar una aplicacion en la propia consola y que no dependiera de las libs FAT que se han demostrado algo inestables.

Esta scene tiene mucahs cosas por pulir aun y eso es lo divertido (aunque a la vez frustrante, cuando algo no te sale)

Lo de que se invente valores, tiene guasa la cosa, aunque a mi me preocupaba mas el tema de las alineaciones, pues es algo que siempre ha dado muchos problemas con estos compiladores.


Por cierto, hace cierto tiempo averigue que es lo que hacia que no pudieramos meter una optimizacion al compilador, superior a -O1, sin que hubiera problemas. Si estas usando -O2 u -O3, añade estas dos cosillas a CFLAGS: -fno-gcse -fno-gcse-lm

Con esas optimizaciones deshabilitadas, puedas ganar incluso el doble de velocidad en la ejecucion de programas en la DS y no te dara un problema muy raro, que hace que se eliminen ciertas partes del codigo y en mi caso me trajo de cabeza con la multitarea.

Yo segun he visto, en las aplicacaciones no suelen meter mas alla de -O0 y asi me pasa ahora: cuando hice mi juego Asteroids and Gems, lo hice sin la optimizacion y ahora gana tal velocidad metiendole un -O3, que se hace injugable (va el doble de deprisa, como poco y eso que el juego utiliza sincronizacion con la pantalla).

Esto te lo comento, porque si estas usando optimizaciones, que sepas que eso es fuente de problemas, salvo que le metas esas dos opciones que te digo
9 respuestas