Files
Espresso/drivers/fs/fat16.c
2025-06-27 14:48:06 -05:00

249 lines
7.4 KiB
C

#include <drivers/ide.h>
#include <string.h>
#include <stdio.h>
#include <fs/fat16.h>
static fat16_t fat;
#pragma pack(push, 1)
typedef struct {
uint8_t jmp[3];
char oem[8];
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t fat_count;
uint16_t root_entries;
uint16_t total_sectors_short;
uint8_t media_desc;
uint16_t fat_size_sectors;
uint16_t sectors_per_track;
uint16_t heads;
uint32_t hidden_sectors;
uint32_t total_sectors_long;
// Extended Boot Record...
} fat16_bpb_t;
typedef struct {
char name[8];
char ext[3];
uint8_t attr;
uint8_t reserved;
uint8_t create_time_tenth;
uint16_t create_time;
uint16_t create_date;
uint16_t access_date;
uint16_t first_cluster_hi;
uint16_t write_time;
uint16_t write_date;
uint16_t first_cluster_lo;
uint32_t size;
} fat16_direntry_raw_t;
#pragma pack(pop)
static uint8_t sector_buffer[512];
uint32_t cluster_to_lba(uint16_t cluster)
{
return fat.data_start + ((cluster - 2) * fat.sectors_per_cluster);
}
int32_t fat16_init(uint8_t drive)
{
if (ide_read48(drive, 0, 1, sector_buffer) < 0)
{
return -1;
}
fat16_bpb_t* bpb = (fat16_bpb_t*)sector_buffer;
fat.drive = drive;
fat.bytes_per_sector = bpb->bytes_per_sector;
fat.sectors_per_cluster = bpb->sectors_per_cluster;
fat.root_entry_count = bpb->root_entries;
fat.fat_start = bpb->reserved_sectors;
fat.root_dir_start = fat.fat_start + bpb->fat_count * bpb->fat_size_sectors;
fat.data_start = fat.root_dir_start + ((bpb->root_entries * 32 + fat.bytes_per_sector - 1) / fat.bytes_per_sector);
fat.current_dir_cluster = 0;
return 0;
}
uint16_t fat16_get_fat_entry(uint16_t cluster)
{
uint32_t fat_offset = cluster * 2;
uint32_t fat_sector = fat.fat_start + (fat_offset / fat.bytes_per_sector);
uint32_t ent_offset = fat_offset % fat.bytes_per_sector;
if (ide_read48(fat.drive, fat_sector, 1, sector_buffer) < 0)
{
return 0xFFFF;
}
return *(uint16_t*)&sector_buffer[ent_offset];
}
uint16_t fat16_alloc_cluster(void)
{
for (uint16_t cluster = 2; cluster < 0xFFF0; ++cluster) {
uint16_t entry = fat16_get_fat_entry(cluster);
if (entry == 0x0000) {
// Mark as end of chain
uint32_t fat_offset = cluster * 2;
uint32_t fat_sector = fat.fat_start + (fat_offset / fat.bytes_per_sector);
uint32_t ent_offset = fat_offset % fat.bytes_per_sector;
if (ide_read48(fat.drive, fat_sector, 1, sector_buffer) < 0) return 0;
*(uint16_t*)&sector_buffer[ent_offset] = 0xFFFF;
if (ide_write48(fat.drive, fat_sector, 1, sector_buffer) < 0) return 0;
return cluster;
}
}
return 0;
}
int32_t fat16_list_dir(fat16_dir_entry_t* entries, size_t max_entries)
{
size_t count = 0;
if (fat.current_dir_cluster == 0)
{
uint16_t root_sectors = ((fat.root_entry_count * 32) + (fat.bytes_per_sector - 1)) / fat.bytes_per_sector;
for (uint32_t i = 0; i < root_sectors && count < max_entries; ++i)
{
if (ide_read48(fat.drive, fat.root_dir_start + i, 1, sector_buffer) < 0)
{
return -1;
}
fat16_direntry_raw_t* de = (fat16_direntry_raw_t*)sector_buffer;
for (int32_t j = 0; j < 512 / 32 && count < max_entries; ++j, ++de)
{
if (de->name[0] == 0x00 || de->name[0] == 0xE5)
{
continue;
}
memcpy(entries[count].name, de->name, 8);
entries[count].name[8] = '\0';
entries[count].attr = de->attr;
entries[count].cluster = de->first_cluster_lo;
entries[count].size = de->size;
count++;
}
}
}
/* TODO: Add support for listing subdirectories if current_dir_cluster != 0 */
return count;
}
int32_t fat16_read_file(const char* filename, void* buffer, size_t max_size) {
fat16_dir_entry_t entries[64];
int32_t n = fat16_list_dir(entries, 64);
for (int32_t i = 0; i < n; ++i) {
if (!(entries[i].attr & FAT16_ATTR_DIRECTORY) &&
strncmp(entries[i].name, filename, FAT16_MAX_FILENAME) == 0) {
uint16_t cluster = entries[i].cluster;
uint8_t* out = (uint8_t*)buffer;
size_t remaining = entries[i].size < max_size ? entries[i].size : max_size;
while (cluster >= 0x0002 && cluster < 0xFFF8 && remaining > 0) {
uint32_t lba = cluster_to_lba(cluster);
size_t to_read = fat.bytes_per_sector * fat.sectors_per_cluster;
if (ide_read48(fat.drive, lba, fat.sectors_per_cluster, sector_buffer) < 0)
return -1;
size_t copy_size = (remaining < to_read) ? remaining : to_read;
memcpy(out, sector_buffer, copy_size);
out += copy_size;
remaining -= copy_size;
cluster = fat16_get_fat_entry(cluster);
}
return (int32_t)(out - (uint8_t*)buffer);
}
}
return -1;
}
int32_t fat16_write_file(const char* filename, const void* data, size_t size) {
fat16_dir_entry_t entries[64];
int32_t n = fat16_list_dir(entries, 64);
for (int32_t i = 0; i < n; ++i) {
if (strncmp(entries[i].name, filename, FAT16_MAX_FILENAME) == 0) {
uint16_t cluster = entries[i].cluster;
const uint8_t* src = (const uint8_t*)data;
size_t remaining = size;
while (remaining > 0) {
uint32_t lba = cluster_to_lba(cluster);
size_t cluster_bytes = fat.sectors_per_cluster * fat.bytes_per_sector;
size_t to_write = remaining < cluster_bytes ? remaining : cluster_bytes;
memset(sector_buffer, 0, cluster_bytes); // Clear old data
memcpy(sector_buffer, src, to_write);
if (ide_write48(fat.drive, lba, fat.sectors_per_cluster, sector_buffer) < 0)
return -1;
src += to_write;
remaining -= to_write;
// Last chunk?
if (remaining == 0)
break;
// Check next cluster
uint16_t next = fat16_get_fat_entry(cluster);
if (next >= 0xFFF8 || next == 0x0000) {
// Need to allocate a new cluster
uint16_t new_cluster = fat16_alloc_cluster();
if (new_cluster == 0) return -1;
// Update FAT to point to new cluster
uint32_t fat_offset = cluster * 2;
uint32_t fat_sector = fat.fat_start + (fat_offset / fat.bytes_per_sector);
uint32_t ent_offset = fat_offset % fat.bytes_per_sector;
if (ide_read48(fat.drive, fat_sector, 1, sector_buffer) < 0) return -1;
*(uint16_t*)&sector_buffer[ent_offset] = new_cluster;
if (ide_write48(fat.drive, fat_sector, 1, sector_buffer) < 0) return -1;
next = new_cluster;
}
cluster = next;
}
return (int32_t)size;
}
}
return -1;
}
int32_t fat16_change_dir(const char* dirname)
{
fat16_dir_entry_t entries[64];
int32_t n = fat16_list_dir(entries, 64);
for (int32_t i = 0; i < n; ++i)
{
if ((entries[i].attr & FAT16_ATTR_DIRECTORY) && strncmp(entries[i].name, dirname, FAT16_MAX_FILENAME) == 0)
{
fat.current_dir_cluster = entries[i].cluster;
return 0;
}
}
return -1;
}