/**
* @file fat32.h
* A very basic FAT32 reading implementation
*
* Copyright (C) 2018 Clyne Sullivan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
static uint32_t partition_lba = 0;
static uint8_t SectorsPerCluster;
static uint32_t RootDirCluster;
static uint32_t ClustersLBA;
static uint32_t FATStartLBA;
static uint8_t *RootDir = 0;
int fat_find(void)
{
uint8_t *block = malloc(512);
// find FAT partition
sd_read(block, 0, 512);
for (unsigned int i = 450; i < 450 + 64; i += 16) {
if (block[i] == 0x0B || block[i] == 0x0C) {
partition_lba = *((uint32_t *)(block + i + 4));
break;
}
}
if (partition_lba == 0)
return 0;
// read FAT volume id
sd_read(block, partition_lba, 512);
SectorsPerCluster = block[0x0D];
uint16_t ReservedSectors = *((uint16_t *)(block + 0x0E));
uint32_t SectorsPerFAT = *((uint32_t *)(block + 0x24));
RootDirCluster = *((uint32_t *)(block + 0x2C));
FATStartLBA = partition_lba + ReservedSectors;
ClustersLBA = FATStartLBA + 2 * SectorsPerFAT;
free(block);
return 1;
}
uint32_t fat_cluster2lba(uint32_t cluster)
{
return ClustersLBA + (cluster - 2) * SectorsPerCluster;
}
int fat_namecmp(const char *fatname, const char *name)
{
for (unsigned int i = 0; i < 8; i++) {
if (name[i] == '.' || name[i] == '\0')
break;
if (toupper(fatname[i]) != toupper(name[i]))
return 0;
}
return 1;
}
file_t *fat_findfile(const char *name)
{
if (RootDir == 0) {
RootDir = sd_read(malloc(512 * SectorsPerCluster),
fat_cluster2lba(RootDirCluster), 512 * SectorsPerCluster);
}
uint8_t *block = RootDir;
for (unsigned int i = 0; block[i * 32] != 0; i++) {
if (block[i * 32] == 0xE5 || (block[i * 32 + 11] & 0x0F) == 0x0F)
continue;
if (fat_namecmp((char *)(block + i * 32), name)) {
uint32_t size = *((uint32_t *)(block + i * 32 + 28));
uint32_t start = *((uint16_t *)(block + i * 32 + 20))
<< 16 | *((uint16_t *)(block + i * 32 + 26));
file_t *file = malloc(sizeof(file_t));
file->size = size;
file->start = start;
return file;
}
}
return 0;
}
char *fat_getname(uint32_t index)
{
if (RootDir == 0) {
RootDir = sd_read(malloc(512 * SectorsPerCluster),
fat_cluster2lba(RootDirCluster), 512 * SectorsPerCluster);
}
uint32_t idx = 0;
uint8_t *block = RootDir;
for (unsigned int i = 0; block[i * 32] != 0; i++) {
if (block[i * 32] == 0xE5 || (block[i * 32 + 11] & 0x0F) == 0x0F)
continue;
if (idx == index) {
char *name = strncpy(malloc(12), (char *)(block + i * 32), 11);
name[11] = '\0';
return name;
}
idx++;
}
return 0;
}
char *fat_readfile(file_t *file)
{
if (file == 0)
return 0;
uint8_t *block = malloc(512);
uint32_t start = file->start;
uint8_t *buffer = malloc(file->size + 1);
uint32_t offset = 0;
buffer[file->size] = '\0';
while (start != 0) {
// find in FAT
sd_read(block, FATStartLBA + (start / 128), 512);
uint32_t next = ((uint32_t *)block)[start % 128];
// read start cluster
uint32_t size = 512 * SectorsPerCluster;
if (file->size - offset < size)
size = file->size - offset;
sd_read(buffer, fat_cluster2lba(start), size);
offset += size;
if ((next & 0x0FFFFFFF) == 0x0FFFFFFF)
start = 0;
else
start = next;
}
free(block);
return (char *)buffer;
}