/*#######################################################################################
** FAT for AVR (MMC/SD) 
** 
** Copyright (C) 2004 Ulrich Radig
** 
** Bei Fragen und Verbesserungen wendet euch per EMail an
** 
** mail@ulrichradig.de
** 
** oder im Forum meiner Web Page : www.ulrichradig.de
** 
** 
** Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen der 
** GNU General Public License, wie von der Free Software Foundation veroeffentlicht, 
** weitergeben und/oder modifizieren, entweder gemaess Version 2 der Lizenz oder 
** (nach Ihrer Option) jeder spaeteren Version. 
** 
** Die Veroeffentlichung dieses Programms erfolgt in der Hoffnung, 
** dass es Ihnen von Nutzen sein wird, aber OHNE IRGENDEINE GARANTIE, 
** sogar ohne die implizite Garantie der MARKTREIFE oder der VERWENDBARKEIT 
** FUER EINEN BESTIMMTEN ZWECK. Details finden Sie in der GNU General Public License. 
** 
** Sie sollten eine Kopie der GNU General Public License zusammen mit diesem 
** Programm erhalten haben. 
** Falls nicht, schreiben Sie an die Free Software Foundation, 
** Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 
** #######################################################################################*/
#include <string.h>
#include <avr/pgmspace.h>       // 12.03.2010 li
#include "fat.h"
#include "mmc.h"
#include "lcd.h"

                                // wichtige Konstanten
unsigned char cluster_size;
unsigned int  fat_offset;
unsigned int  cluster_offset;
unsigned int  volume_boot_record_addr;


/***************************************************************************
** Auslesen Cluster Size der MMC/SD Karte und Speichern ins EEprom
** Auslesen Cluster Offset der MMC/SD Karte und Speichern ins EEprom
*****************************************************************************/
void fat_init (void)
{
   struct BootSec *bootp;     //Zeiger auf Bootsektor Struktur
   char c;
   prog_char *addr;
   static char pgm_fat1[] PROGMEM   = "MBR Sig. found\n";
   static char pgm_fat2[] PROGMEM   = "MBR Sig. not found\n";
   static char pgm_fat3[] PROGMEM   = "VBR Sig. found\n";
   static char pgm_fat4[] PROGMEM   = "VBR Sig. not found\n";

   unsigned char Buffer[ BlockSize ];

                              // volume_boot_record_addr = fat_addr (Buffer); 
   mmc_read_sector ( MASTER_BOOT_RECORD, Buffer); //Read Master Boot Record 
   if (Buffer[510] == 0x55 && Buffer[511] == 0xAA)
   {
      addr = (prog_char*)&pgm_fat1;
      c = pgm_read_byte( addr++ );       // MBR Sig. found
      do
      {      
         lcd_data( c );
         c = pgm_read_byte( addr++ );     
      } while( c ); 
   }
   else
   {
      addr = (prog_char*)&pgm_fat2;
      c = pgm_read_byte( addr++ );       // MBR Sig. not found
      do
      {      
         lcd_data( c );
         c = pgm_read_byte( addr++ );     
      } while( c ); 
      while(1);
   }

   volume_boot_record_addr = Buffer[VBR_ADDR] + (Buffer[VBR_ADDR+1] << 8);
    
   mmc_read_sector (volume_boot_record_addr,Buffer);
   if (Buffer[510] == 0x55 && Buffer[511] == 0xAA)
   {
      addr = (prog_char*)&pgm_fat3;
      c = pgm_read_byte( addr++ );       // VBR Sig. found
      do
      {      
         lcd_data( c );
         c = pgm_read_byte( addr++ );     
      } while( c );  
   }
   else
   {
      addr = (prog_char*)&pgm_fat4;
      c = pgm_read_byte( addr++ );       // VBR Sig. not found
      do
      {      
         lcd_data( c );
         c = pgm_read_byte( addr++ );     
      } while( c ); 
      volume_boot_record_addr = MASTER_BOOT_RECORD;   //<- added by Hennie
      mmc_read_sector( MASTER_BOOT_RECORD, Buffer );  //Read Master Boot Record 
   }   
    
   bootp=(struct BootSec *)Buffer;
   cluster_size = bootp->BPB_SecPerClus;
   fat_offset = bootp->BPB_RsvdSecCnt;

   cluster_offset = ((bootp->BPB_BytesPerSec * 32)/BlockSize); 
   cluster_offset += fat_root_dir_addr(Buffer);
}



/****************************************************************************
** Auslesen der Adresse des First Root Directory von Volume Boot Record
****************************************************************************/
unsigned int fat_root_dir_addr( unsigned char *Buffer ) 
{
   struct BootSec *bootp;     // Zeiger auf Bootsektor Struktur
   unsigned int FirstRootDirSecNum;
   
                              // auslesen des Volume Boot Record von der MMC/SD Karte 
   mmc_read_sector (volume_boot_record_addr,Buffer);
   bootp=(struct BootSec *)Buffer;

                              // berechnet den ersten Sector des Root Directory
   FirstRootDirSecNum = ( bootp->BPB_RsvdSecCnt + (bootp->BPB_NumFATs * bootp->BPB_FATSz16));

   FirstRootDirSecNum += volume_boot_record_addr;
   
   return( FirstRootDirSecNum );
}



/****************************************************************************
** Ausgabe des angegebenen Directory Eintrag in Entry_Count
** ist kein Eintrag vorhanden, ist der Eintrag im 
** Rueckgabe Cluster 0xFFFF. Es wird immer nur ein Eintrag ausgegeben
** um Speicherplatz zu sparen um es auch fuer kleine Atmels zu benutzen
*****************************************************************************/
unsigned int fat_read_dir_ent ( unsigned int dir_cluster,  //Angabe Dir Cluster
                                unsigned char Entry_Count, //Angabe welcher Direintrag
                                unsigned long *Size,       //Rueckgabe der Filesize
                                unsigned char *Dir_Attrib, //Rueckgabe des Dir Attributs
                                unsigned char *Buffer)     //Working Buffer
{
   unsigned char *pointer;
   unsigned int TMP_Entry_Count = 0;
   unsigned long Block = 0;
   struct DirEntry *dir;     // Zeiger auf einen Verzeichniseintrag

   pointer = Buffer;

   if (dir_cluster == 0)
   {
      Block = fat_root_dir_addr(Buffer);
   }
   else
   {
                             // Berechnung des Blocks aus BlockCount und Cluster aus FATTabelle
                             // Berechnung welcher Cluster zu laden ist
                             // Auslesen der FAT - Tabelle
      fat_load (dir_cluster,&Block,Buffer);         
      Block = ((Block-2) * cluster_size) + cluster_offset;
   }

                             // auslesen des gesamten Root Directory
   for (unsigned int blk = Block;;blk++)
   {
      mmc_read_sector (blk,Buffer); // Lesen eines Blocks des Root Directory
      for (unsigned int a=0;a<BlockSize; a = a + 32)
      {
         dir=(struct DirEntry *)&Buffer[a]; //Zeiger auf aktuellen Verzeichniseintrag holen
       
         if (dir->DIR_Name[0] == 0) // Kein weiterer Eintrag wenn erstes Zeichen des Namens 0 ist
         {
            return (0xFFFF);
         }
         
                                    // Pruefen ob es ein 8.3 Eintrag ist
                                    // Das ist der Fall wenn es sich nicht um einen Eintrag fuer lange Dateinamen
                                    // oder um einen als geloescht markierten Eintrag handelt.
         if ( (dir->DIR_Attr != ATTR_LONG_NAME) && (dir->DIR_Name[0] != DIR_ENTRY_IS_FREE) ) 
         {                          
            if (TMP_Entry_Count == Entry_Count)    // Ist es der gewuenschte Verzeichniseintrag
            {
                                    // Speichern des Verzeichnis Eintrages in den Rueckgabe Buffer
               for( unsigned char b=0; b < 11 ; b++)
               {
                  if ( dir->DIR_Name[b] != SPACE )
                  {
                     if ( b == 8 )
                     {
                        *pointer++= '.';
                     }
                     *pointer++ = dir->DIR_Name[b];
                  }
               }                 
               *pointer++ = '\0';
               *Dir_Attrib = dir->DIR_Attr;                                    
               *Size = dir->DIR_FileSize;         // Speichern der Filegroesse
               dir_cluster = dir->DIR_FstClusLO;  // Speichern des Clusters des Verzeichniseintrages
               return(dir_cluster);               // Eintrag gefunden Ruecksprung mit Cluster File Start
            }
            TMP_Entry_Count++;
         }
      }
   }
   return (0xFFFF);                               //Kein Eintrag gefunden ret=0xFFFF
}


/****************************************************************************
** Auslesen der Cluster fuer ein File aus der FAT
** in den Buffer(512Byte).
** Bei einer 128MB MMC/SD Karte ist die Cluster groesse normalerweise 16KB gross
** das bedeutet das File kann max. 4MByte gross sein.
** Bei groesseren Files muss der Buffer groesser definiert
** werden! (Ready)
** Cluster = Start Clusterangabe aus dem Directory 
****************************************************************************/
void fat_load (   unsigned int Cluster,       // Angabe Startcluster
                  unsigned long *Block,
                  unsigned char *TMP_Buffer)  // Workingbuffer
{
                                    // Zum Ueberpruefen ob der FAT Block schon geladen wurde
   unsigned int FAT_Block_Store = 0;   

                                    // Byte Adresse innerhalb des Fat Blocks
   unsigned int FAT_Byte_Addresse;  

                                    // FAT Block Adresse
   unsigned int FAT_Block_Addresse;
   
                                    // Berechnung fuer den ersten FAT Block (FAT Start Addresse)
   for (unsigned int a = 0;;a++)
   {  
      if (a == *Block)
      {
         *Block = (0x0000FFFF & Cluster);
         return;
      }      
      if (Cluster == 0xFFFF)
      {
         break;                     // Ist das Ende des Files erreicht Schleife beenden
      }
                                    // Berechnung des Bytes innerhalb des FAT Block´s
      FAT_Byte_Addresse = (Cluster*2) % BlockSize;
         
                                    // Berechnung des Blocks der gelesen werden muss
      FAT_Block_Addresse = ((Cluster*2) / BlockSize) + volume_boot_record_addr + fat_offset;  
                                    // Lesen des FAT Blocks
                                    // Ueberpruefung ob dieser Block schon gelesen wurde
      if (FAT_Block_Addresse != FAT_Block_Store)
      {
         FAT_Block_Store = FAT_Block_Addresse;
                                    // Lesen des FAT Blocks
         mmc_read_sector (FAT_Block_Addresse,TMP_Buffer);   
      }

                                    // Lesen der naechsten Clusternummer
      Cluster = (TMP_Buffer[FAT_Byte_Addresse + 1] << 8) + 
               TMP_Buffer[FAT_Byte_Addresse];      
   }
   return;
}



/****************************************************************************
** Lesen eines 512Bytes Blocks von einem File
****************************************************************************/
void fat_read_file ( unsigned int Cluster,     // Angabe des Startclusters vom File
                     unsigned char *Buffer,    // Workingbuffer
                     unsigned long BlockCount) // Angabe welcher Bock vom File geladen 
                                               // werden soll a 512 Bytes
{
                                    // Berechnung des Blocks aus BlockCount und Cluster aus FATTabelle
                                    // Berechnung welcher Cluster zu laden ist
   
   unsigned long Block = (BlockCount/cluster_size);
   
                                    // Auslesen der FAT - Tabelle
   fat_load (Cluster,&Block,Buffer);          
   Block = ((Block-2) * cluster_size) + cluster_offset;
                                    // Berechnung des Blocks innerhalb des Cluster
   Block += (BlockCount % cluster_size);
                                   // Read Data Block from Device
   mmc_read_sector (Block,Buffer);  
   return;
}



/****************************************************************************
** write eines 512Bytes Blocks von einem File
****************************************************************************/
void fat_write_file ( unsigned int cluster,     // Angabe des Startclusters vom File
                      unsigned char *buffer,    // Workingbuffer
                      unsigned long blockCount) // Angabe welcher Bock vom File gespeichert 
                                                // werden soll a 512 Bytes
{
                                    // Berechnung des Blocks aus BlockCount und Cluster aus FATTabelle
                                    // Berechnung welcher Cluster zu speichern ist
   unsigned char tmp_buffer[513];   
   unsigned long block = (blockCount/cluster_size);
   
                                    // Auslesen der FAT - Tabelle
   fat_load (cluster,&block,tmp_buffer);         
   block = ((block-2) * cluster_size) + cluster_offset;
                                    // Berechnung des Blocks innerhalb des Cluster
   block += (blockCount % cluster_size);
                                    // Write Data Block to Device
   mmc_write_sector (block,buffer); 
   return;
}



/****************************************************************************
** Sucht ein File im Directory
****************************************************************************/
unsigned char fat_search_file ( unsigned char *File_Name, // Name des zu suchenden Files
                                unsigned int *Cluster,    // Angabe Dir Cluster welches
                                                          // durchsucht werden soll
                                                          // und Rueckgabe des clusters
                                                          // vom File welches gefunden
                                                          // wurde
                                unsigned long *Size,      // Rueckgabe der File Groesse
                                unsigned char *Dir_Attrib,// Rueckgabe des Dir Attributs
                                unsigned char *Buffer)    // Working Buffer
{
   unsigned int Dir_Cluster_Store = *Cluster;
   for (  unsigned char a = 0; a < 100; a++ )
   {
      *Cluster = fat_read_dir_ent( Dir_Cluster_Store, a, Size, Dir_Attrib, Buffer );
      if (*Cluster == 0xffff)
      {
         return( 0 );               // File not Found
      }
      if( strcasecmp((char *)File_Name,(char *)Buffer) == 0)
      {
         return(1);                 // File Found
      }
   }
   return(2);                       // Error
}
