Update: V2: corregidos algunos bugs y añadido soporte para analizar cuantos cluster se pueden cargar de forma consecutiva (valido solo para FAT 16/32)
Si sois usuarios de DevkitPro y os habeis visto en la necesidad de utilizar la libfat de "Chishm" alguna vez, tal vez os interese echar un ojo a esto, que aunque está pensado para optimizar la lectura/escritura en Wii mediante dispositivo USB, añade una serie de mejoras a la librería que deberian valer para todos los sistemas, salvo que el dispositivo que useis no permita la lectura de multiples sectores, en cuyo caso, deberiais limitar a 1 el parametro "sectorsPerCluster" en
FAT_cache_constructor, pero eso ya seria trabajo vuestro y por supuesto, este hilo puede servir para vuestras aportaciones.
Pero antes, me gustaría dejar unas cuantes cosas bien claras:
- Yo no me he puesto en contacto con "Chishm", ni lo voy a hacer en el futuro (mi nivel de ingles me lo impide y tampoco voy a esperarme a que el autor original adopte o no adopte los cambios, porque estos son importantes para nosotros, pero lo mismo da problemas con un cartucho de GBA, por poner un ejemplo)
- No tengo contacto con los que mantienen Devkitpro, ni lo voy a tener, porque la barrera del idioma sigue siendo importante.
- Esta librería tiene los parches de
Sven para dispostivos USB en Wii y ha sido modificada por
rodries que ha arreglado algunos problemas y en parte ha sido la luz para orientarme en que sentido debía trabajar. Esta pensada para Wii, pero creo que es perfectamente valida para cualquier otra consola (el unico problema puede estar en que el dispositivo no esté preparado para lectura de multiples sectores o para soportar todos los que la librería le pida de golpe, pero eso es facil de arreglar)
Por tanto esta librería es muy posible que no llegue a los canales oficiales, o quizá llegue mas tarde. En todo caso, paso a describir lo que ha cambiado, segun lo he encontrado yo:
En primer lugar, los discos duros agrupan sectores en clusters por que es la forma mas rapida de leer sectores, agrupandolos. Tambien es comun el uso de caches para evitar leer repetidamente los clusters mas utilizado y que tiene especial importancia cuando se realizan pequeñas lecturas y escrituras.
Pero el creador de la librería, por alguna razón decidió dividir la caché en sectores y trabajar de esa forma, por lo que yo he tenido que cambiar esas forma de trabajar por otra que agrupe clusters enteros, o en el peor de los casos, tomar una porción de cluster como medida, que no sea desmasiado grande para suponer un desperdicio de memoria en sistemas pequeños como la GBA, pero tampoco tan pequeña para que se3 vuelva ineficaz.
Además, en vez de utilizar solo una caché, ahora se utilizan dos:
-partition->cache: se utiliza para la lecturas/escrituras de entradas FAT. Puede alojar clusters enteros, pero si el tamaño de cluster supera los 8 sectores, entonces se toma 8 como medida parcial. Desde el punto de vista de la librería, eso da igual, que no haya correspondecia con el tamaño del cluster del disco y evita que se superen los 32KB por entrada. En un sistema pequeño, fatInit(2,false) reservaría un maximo 8KB de uso para esta cache, mientras que fatInit(8,false), lo usual en Wii, reservaría un maximo de 32KB para ésta cache. Poner un numero mayor de paginas (sin ser exagerado), puede acelerar el acceso a las tablas FAT, evidentemente.
-partition->racache: se utiliza para las lecturas/escrituras de ficheros (es decir, los datos que manejaremos nosotros) y por defecto se asigna en fatInit() 2 paginas de 64 sectores, por lo que dejandolo así gastariamos 64KB. Sin embargo, podemos ajustar esto a nuestra conveniencia si usamos la funcion fatEnableReadAhead(). Por ejemplo, fatEnableReadAhead(PI_USBSTORAGE, 6, 64); es lo que suele usar
rodries y corresponde con 6 paginas de 64 sectores, lo que equivale a gastar unos 192KB en la cache.
Teneis que tener claro una cosa: en un dispositivo USB en Wii, leer 64 sectores de una tacada, es mas rapido que leerlos de 8 en 8 hasta completar los 64, pero si el cluster tiene un tamaño de 8 sectores y el fichero está fragmentado, la estrategia de leer 64 sectores de una tacada, no es buena y redundará en perdida de eficacia. Por ello tengo previsto mas adelante, meter un sistema que mire el numero de entradas FAT disponibles de forma consecutiva y que aloje los cluster precisos para no penalizar.
Las caches están preparadas para trabajar en bloques de clusters(grupos, paginas... como mas os guste) pero trabajan al mismo tiempo a nivel de clusters.
Es decir: a la hora de elegir que clusters debo descartar de la cache por ser los mas antiguos en ser usados y necesitar cargar clusters nuevos, se va a tener en cuenta un contador de grupo que se refresca cada vez que se utiliza uno de los clusters que forma parte de ese bloque. Pero sin embargo, a la hora de buscar clusters, se hace de forma individual, al igual que cuando sea necesario escribir un cluster, se escribira ese cluster o los que estén consecutivos a el que tambien hayan sido modificados, de forma individual o en grupo, si se puede optimizar.
En la recarga del bloque de la cache, si se producen solapamientos de clusters, se marcarán como clusters vacios los nuevos clusters cargados coincidentes, para evitar paradojas.
Una cosa que he solucionado desde la versión de partida, es que solo estaba preparada para abrir un fichero como lectura o como escritura, pero no estaba preparada para leer un fichero como lectura/escritura y mantener la coherencia entre el dispositivo y la cache. Si abrias un fichero como fopen(name,"rb+") se podia armar una buena porque en las escrituras se volcaban los datos directamente, sin conservar lo que hubiera que conservar (primero se debe leer y luego, escribir para hacerlo correctamente)
Como leer y escribir un sector, es mas lento que escribirlo directamente, he tenido en cuenta que si se abre un fichero para solo escritura, si bien es necesario mantener los cambios en la caché, no es necesario leer desde el dispositivo. Por eso en estos casos, activo un flag en la cache "only_writable" que anula el refresco desde el dispositivo de la cache.
Para adaptar la librería al modo de trabajar en bloques, en las funciones de lectura/escritura de sectores, he dividido el sector pasado en dos parametros. Por regla general estas funciones pasan el sector base del cluster, o el sector base del cluster desplazado en una serie de clusters y por otro lado, el offset al sector.
Es por eso que estas funciones tienen un parametro
sector0 y otro
sector: con la combinacion de ambos, puedo agrupar correctamente los sectores por bloques, manteniendo al mismo tiempo la alineacion con respecto al sector de base de los clusters. Suena complejo, pero es lo que hace que los sectores de un cluster se lean de forma grupal sin solaparse y correctamente alineados.
Por otro lado, he corregido algunos otros problemas: por ejemplo, en algunas funciones que implican a ficheros es conveniente flushear toda la cache e incluso, invalidarla. Tambien es recomendable desmontar la particion antes de salir del programa, para asegurarse que todas las entradas fat modificadas, se escriban (de no hacerlo, se perderia informacion importante, mucho ojo a esto, porque si no cerramos un fichero que estemos escribiendo, pueden haber datos residuales que no se han actualizado).
Por ello si desmontas la particion, aunque la funcion falle por tener un fichero abierto, los datos seran flusheados al dispositivo, para evitar este tipo de errores.
Por otro lado, algunos programas requieren los bytes libres disponibles en el dispositivo. Esta operación puede tardar unos segundos, por lo que para evitar que cada vez que la llamemos, se quede "pillado", he añadido un flag que hace que solo refresque los datos, si necesita refrescarlos, devolviendo el espacio libre que se leyó previamente.
Por otro lado, he añadido un sistema de reintentos en caso de error en la lectura/escritura del dispositivo, que yo creo que vendrá muy bien y qu eno se porque coño no estaba implementado ya, pero bueno
.
En fin, creo que esto resume mas o menos todo lo que he trabajado con ello: he hecho test abriendo hasta doscientos ficheros que se combinaban entre si y con operaciones de creacion/borrado de directorios, ficheros, etc y no he encontrado ninguna sola inconsistencia con el scandisk, asi que parece que al menos esto, es estable.
Como curiosidad, en Wii leyendo de dispositivo USB (USB 1.1 ) me arroja estos datos, para la escritura/lectura de un fichero de 8MB
Escritura creando fichero:
lectura/escritura: fopen(name,"wb+"); 323672 bytes/segundo
solo escritura: fopen(name,"wb"); 562804 bytes/segundo (como se nota el modo de cache only_writable
)
Lectura de fichero: 759150 bytes/segundo
Todo eso utilizando :
fatInit(8, false);
fatEnableReadAhead(PI_USBSTORAGE, 6, 64); // antes deberiamos comprobar que la particion está montada,claro
Metodo de inicializacion en Wii#include <fat.h>
int have_device=0;
....
{
DIR_ITER *dir;
char fat[7] = "fat0:/";
fat[3] = 48+PI_INTERNAL_SD;
dir = diropen(fat);
if (dir) {dirclose(dir); have_device|=1;fatEnableReadAhead(PI_INTERNAL_SD, 6, 64);}
fat[3] = 48+PI_USBSTORAGE;
dir = diropen(fat);
if (dir) {dirclose(dir); have_device|=2;fatEnableReadAhead(PI_USBSTORAGE, 6, 64);}
}
if(havedevice & 1) fatSetDefaultInterface(PI_INTERNAL_SD); // asigna "fat:" a la SD
if(havedevice & 2) fatSetDefaultInterface(PI_USBSTORAGE); // asigna "fat:" al pendrive
Descarga: libfat vs Hermes (V2)