+/*
+ * ao_fat_cluster_set_size
+ *
+ * Set the number of clusters in the specified chain,
+ * freeing extra ones or alocating new ones as needed
+ *
+ * Returns AO_FAT_BAD_CLUSTER on allocation failure
+ */
+
+static cluster_t
+ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size)
+{
+ cluster_t clear_cluster = 0;
+
+ if (size == 0) {
+ clear_cluster = first_cluster;
+ first_cluster = 0;
+ } else {
+ cluster_t have;
+ cluster_t last_cluster = 0;
+ cluster_t next_cluster;
+
+ /* Walk the cluster chain to the
+ * spot where it needs to change. That
+ * will either be the end of the chain (in case it needs to grow),
+ * or after the desired number of clusters, in which case it needs to shrink
+ */
+ next_cluster = first_cluster;
+ for (have = 0; have < size; have++) {
+ last_cluster = next_cluster;
+ next_cluster = ao_fat_entry_read(last_cluster);
+ if (!ao_fat_cluster_valid(next_cluster))
+ break;
+ }
+
+ if (have == size) {
+ /* The file is large enough, truncate as needed */
+ if (ao_fat_cluster_valid(next_cluster)) {
+ /* Rewrite that cluster entry with 0xffff to mark the end of the chain */
+ clear_cluster = ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER);
+ filesystem_full = 0;
+ } else {
+ /* The chain is already the right length, don't mess with it */
+ ;
+ }
+ } else {
+ cluster_t need;
+ cluster_t free;
+
+ if (filesystem_full)
+ return AO_FAT_BAD_CLUSTER;
+
+ if (next_free < 2 || number_cluster <= next_free) {
+ next_free = 2;
+ fsinfo_dirty = 1;
+ }
+
+ /* See if there are enough free clusters in the file system */
+ need = size - have;
+
+#define loop_cluster for (free = next_free; need > 0;)
+#define next_cluster \
+ if (++free == number_cluster) \
+ free = 2; \
+ if (free == next_free) \
+ break; \
+
+ loop_cluster {
+ if (!ao_fat_entry_read(free))
+ need--;
+ next_cluster;
+ }
+ /* Still need some, tell the user that we've failed */
+ if (need) {
+ filesystem_full = 1;
+ return AO_FAT_BAD_CLUSTER;
+ }
+
+ /* Now go allocate those clusters and
+ * thread them onto the chain
+ */
+ need = size - have;
+ loop_cluster {
+ if (!ao_fat_entry_read(free)) {
+ next_free = free + 1;
+ if (next_free >= number_cluster)
+ next_free = 2;
+ fsinfo_dirty = 1;
+ if (last_cluster)
+ ao_fat_entry_replace(last_cluster, free);
+ else
+ first_cluster = free;
+ last_cluster = free;
+ need--;
+ }
+ next_cluster;
+ }
+#undef loop_cluster
+#undef next_cluster
+ /* Mark the new end of the chain */
+ ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER);
+ }
+ }
+
+ /* Deallocate clusters off the end of the file */
+ if (ao_fat_cluster_valid(clear_cluster))
+ ao_fat_free_cluster_chain(clear_cluster);
+ return first_cluster;
+}
+
+/* Start using a root directory entry */
+static uint8_t *
+ao_fat_root_get(dirent_t e)
+{
+ offset_t byte = e * DIRENT_SIZE;
+ sector_t sector = byte >> SECTOR_SHIFT;
+ cluster_offset_t offset = byte & SECTOR_MASK;
+ uint8_t *buf;
+
+ if (fat32) {
+ cluster_t cluster_distance = sector / sectors_per_cluster;
+ sector_t sector_index = sector % sectors_per_cluster;
+ cluster_t cluster = ao_fat_cluster_seek(root_cluster, cluster_distance);
+
+ if (ao_fat_cluster_valid(cluster))
+ sector = data_start + (cluster-2) * sectors_per_cluster + sector_index;
+ else
+ return NULL;
+ } else {
+ if (e >= root_entries)
+ return NULL;
+ sector = root_start + sector;
+ }
+
+ buf = ao_fat_sector_get(sector);
+ if (!buf)
+ return NULL;
+ return buf + offset;
+}
+
+/* Finish using a root directory entry, 'w' is 1 if modified */
+static void
+ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write)
+{
+ cluster_offset_t offset = ((e * DIRENT_SIZE) & SECTOR_MASK);
+ uint8_t *buf = root - offset;
+
+ ao_fat_sector_put(buf, write);
+}
+
+/*
+ * ao_fat_root_extend
+ *
+ * On FAT32, make the
+ */
+static int8_t
+ao_fat_root_extend(dirent_t ents)
+{
+ offset_t byte_size;
+ cluster_t cluster_size;
+ if (!fat32)
+ return 0;
+
+ byte_size = ents * 0x20;
+ cluster_size = byte_size / bytes_per_cluster;
+ if (ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER)
+ return 1;
+ return 0;
+}
+