From 50b933231ca5e6065f3b548589f693ac7e8e7b82 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= <ondra@ondrovo.com>
Date: Wed, 10 Jun 2015 19:22:51 +0200
Subject: [PATCH] Added great improvements from avr-lib, including huge name
 changes.

---
 blockdev.h       |  66 ++++++++++++++
 fat16.c          | 225 +++++++++++++++++++++++++++++------------------
 fat16.h          | 117 ++++++++++++++----------
 fat16_internal.h |   1 -
 main.c           |  14 +--
 5 files changed, 282 insertions(+), 141 deletions(-)
 create mode 100644 blockdev.h

diff --git a/blockdev.h b/blockdev.h
new file mode 100644
index 0000000..ee422e8
--- /dev/null
+++ b/blockdev.h
@@ -0,0 +1,66 @@
+#pragma once
+
+//
+// Block device interface, somewhat akin to stream.h
+// Used for filesystem implementations.
+//
+
+#include <stdint.h>
+
+/** Abstract block device interface
+ *
+ * Populate an instance of this with pointers to your I/O functions.
+ */
+typedef struct
+{
+	/** Sequential read at cursor
+	 * @param dest destination memory structure
+	 * @param len  number of bytes to load and store in {dest}
+	 */
+	void (*load)(void* dest, const uint16_t len);
+
+
+	/** Sequential write at cursor
+	 * @param src source memory structure
+	 * @param len number of bytes to write
+	 */
+	void (*store)(const void* src, const uint16_t len);
+
+
+	/** Write one byte at cursor
+	 * @param b byte to write
+	 */
+	void (*write)(const uint8_t b);
+
+
+	/** Read one byte at cursor
+	 * @return the read byte
+	 */
+	uint8_t (*read)(void);
+
+
+	/** Absolute seek - set cursor
+	 * @param addr new cursor address
+	 */
+	void (*seek)(const uint32_t addr);
+
+
+	/** Relative seek - move cursor
+	 * @param offset cursor address change
+	 */
+	void (*rseek)(const int16_t offset);
+
+
+
+	/** Flush the data buffer if it's dirty.
+	 *
+	 * Should be called after each sequence of writes,
+	 * to avoid data loss.
+	 *
+	 * Tmplementations that do not need this should provide
+	 * a no-op function.
+	 */
+	void (*flush)(void);
+
+} BLOCKDEV;
+
diff --git a/fat16.c b/fat16.c
index c4f9f63..0866529 100644
--- a/fat16.c
+++ b/fat16.c
@@ -24,7 +24,7 @@ uint16_t next_clu(const FAT16* fat, uint16_t cluster);
 uint32_t clu_offs(const FAT16* fat, uint16_t cluster, uint32_t addr);
 
 /** Read a file entry from directory (dir starting cluster, entry number) */
-void open_file(const FAT16* fat, FAT16_FILE* file, const uint16_t dir_cluster, const uint16_t num);
+void open_file(const FAT16* fat, FFILE* file, const uint16_t dir_cluster, const uint16_t num);
 
 /** Allocate and chain new cluster to a chain starting at given cluster */
 bool append_cluster(const FAT16* fat, const uint16_t clu);
@@ -42,7 +42,7 @@ bool free_cluster_chain(const FAT16* fat, uint16_t clu);
  * Check if there is already a file of given RAW name
  * Raw name - name as found on disk, not "display name".
  */
-bool dir_find_file_raw(FAT16_FILE* dir, const char* fname);
+bool dir_find_file_raw(FFILE* dir, const char* fname);
 
 /** Write a value into FAT */
 void write_fat(const FAT16* fat, const uint16_t cluster, const uint16_t value);
@@ -103,7 +103,10 @@ uint32_t find_bs(const BLOCKDEV* dev)
 			// Verify that the boot sector has a valid signature mark
 			dev->seek(tmp + 510);
 			dev->load(&tmp2, 2);
-			if (tmp2 != 0xAA55) continue; // continue to next entry
+			if (tmp2 != 0xAA55)
+			{
+				continue; // continue to next entry
+			}
 
 			// return absolute MBR address
 			return tmp;
@@ -273,10 +276,10 @@ bool free_cluster_chain(const FAT16* fat, uint16_t clu)
  * Check if there is already a file of given RAW name
  * Raw name - name as found on disk, not "display name".
  */
-bool dir_find_file_raw(FAT16_FILE* dir, const char* fname)
+bool dir_find_file_raw(FFILE* dir, const char* fname)
 {
 	// rewind
-	fat16_first(dir);
+	ff_first(dir);
 
 	do
 	{
@@ -296,7 +299,7 @@ bool dir_find_file_raw(FAT16_FILE* dir, const char* fname)
 			return true; // file is already open.
 		}
 	}
-	while (fat16_next(dir));
+	while (ff_next(dir));
 
 	return false;
 }
@@ -308,7 +311,7 @@ bool dir_find_file_raw(FAT16_FILE* dir, const char* fname)
  * dir_cluster ... directory start cluster
  * num ... entry number in the directory
  */
-void open_file(const FAT16* fat, FAT16_FILE* file, const uint16_t dir_cluster, const uint16_t num)
+void open_file(const FAT16* fat, FFILE* file, const uint16_t dir_cluster, const uint16_t num)
 {
 	// Resolve starting address
 	uint32_t addr;
@@ -394,7 +397,7 @@ void open_file(const FAT16* fat, FAT16_FILE* file, const uint16_t dir_cluster, c
 	}
 
 	// Init cursors
-	fat16_seek(file, 0);
+	ff_seek(file, 0);
 }
 
 
@@ -403,7 +406,7 @@ void open_file(const FAT16* fat, FAT16_FILE* file, const uint16_t dir_cluster, c
  * Write information into a file header.
  * "file" is an open handle.
  */
-void write_file_header(FAT16_FILE* file, const char* fname_raw, const uint8_t attribs, const uint16_t clu_start)
+void write_file_header(FFILE* file, const char* fname_raw, const uint8_t attribs, const uint16_t clu_start)
 {
 	const BLOCKDEV* dev = file->fat->dev;
 
@@ -439,9 +442,12 @@ void write_file_header(FAT16_FILE* file, const char* fname_raw, const uint8_t at
 // =============== PUBLIC FUNCTION IMPLEMENTATIONS =================
 
 /** Initialize a FAT16 handle */
-void fat16_init(const BLOCKDEV* dev, FAT16* fat)
+bool ff_init(const BLOCKDEV* dev, FAT16* fat)
 {
 	const uint32_t bs_a = find_bs(dev);
+
+	if (bs_a == 0) return false;
+
 	fat->dev = dev;
 	read_bs(dev, &(fat->bs), bs_a);
 	fat->fat_addr = bs_a + (fat->bs.reserved_sectors * 512);
@@ -449,6 +455,8 @@ void fat16_init(const BLOCKDEV* dev, FAT16* fat)
 	fat->data_addr = fat->rd_addr + (fat->bs.root_entries * 32); // entry is 32B long
 
 	fat->bs.bytes_per_cluster = (fat->bs.sectors_per_cluster * 512);
+
+	return true;
 }
 
 
@@ -456,7 +464,7 @@ void fat16_init(const BLOCKDEV* dev, FAT16* fat)
  * Move file cursor to a position relative to file start
  * Allows seek past end of file, will allocate new cluster if needed.
  */
-bool fat16_seek(FAT16_FILE* file, uint32_t addr)
+bool ff_seek(FFILE* file, uint32_t addr)
 {
 	const FAT16* fat = file->fat;
 
@@ -504,7 +512,7 @@ bool fat16_seek(FAT16_FILE* file, uint32_t addr)
  * Check if file is a regular file or directory entry.
  * Those files can be shown to user.
  */
-bool fat16_is_regular(const FAT16_FILE* file)
+bool ff_is_regular(const FFILE* file)
 {
 	switch (file->type)
 	{
@@ -522,13 +530,19 @@ bool fat16_is_regular(const FAT16_FILE* file)
 
 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
 
-bool fat16_read(FAT16_FILE* file, void* target, uint32_t len)
+uint16_t ff_read(FFILE* file, void* target, uint16_t len)
 {
 	if (file->cur_abs == 0xFFFF)
-		return false; // file at the end already
+		return 0; // file at the end already
 
 	if (file->cur_rel + len > file->size)
-		return false; // Attempt to read more than what is available
+	{
+		if (file->cur_rel > file->size) return 0;
+		len = file->size - file->cur_rel;
+		//return false; // Attempt to read more than what is available
+	}
+
+	const uint16_t len_orig = len;
 
 	const FAT16* fat = file->fat;
 	const BLOCKDEV* dev = fat->dev;
@@ -562,18 +576,27 @@ bool fat16_read(FAT16_FILE* file, void* target, uint32_t len)
 		len -= chunk;
 	}
 
-	return true;
+	return len_orig;
+}
+
+
+bool ff_write_str(FFILE* file, const char* source)
+{
+	uint16_t len = 0;
+	for (; source[len] != 0; len++);
+
+	return ff_write(file, source, len);
 }
 
 
-bool fat16_write(FAT16_FILE* file, void* source, uint32_t len)
+bool ff_write(FFILE* file, const void* source, uint32_t len)
 {
 	const FAT16* fat = file->fat;
 	const BLOCKDEV* dev = fat->dev;
 
 
 	if (file->cur_abs == 0xFFFF)
-		return false; // file at the end already
+		return false; // file past it's end (rare)
 
 	// Attempt to write past end of file
 	if (file->cur_rel + len >= file->size)
@@ -582,14 +605,14 @@ bool fat16_write(FAT16_FILE* file, void* source, uint32_t len)
 
 		// Seek to the last position
 		// -> fseek will allocate clusters
-		if (!fat16_seek(file, pos_start + len))
+		if (!ff_seek(file, pos_start + len))
 			return false; // error in seek
 
 		// Write starts beyond EOF - creating a zero-filled "hole"
-		if (file->cur_rel > file->size)
+		if (pos_start > file->size + 1)
 		{
 			// Seek to the end of valid data
-			fat16_seek(file, file->size);
+			ff_seek(file, file->size);
 
 			// fill space between EOF and start-of-write with zeros
 			uint32_t fill = pos_start - file->size;
@@ -617,31 +640,44 @@ bool fat16_write(FAT16_FILE* file, void* source, uint32_t len)
 			}
 		}
 
-		// Save new size
-		fat16_resize(file, pos_start + len);
+		// Store new size
+		file->size = pos_start + len;
 
 		// Seek back to where it was before
-		fat16_seek(file, pos_start);
+		ff_seek(file, pos_start);
 	} // (end zerofill)
 
 
 	// write the data
 	while (len > 0)
 	{
-		// How much can be stored in this cluster
-		const uint16_t chunk = MIN(fat->bs.bytes_per_cluster - file->cur_ofs, len);
-
-		// store the chunk
 		dev->seek(file->cur_abs);
-		dev->store(source, chunk);
+		uint16_t chunk;
 
-		// advance cursors
-		file->cur_abs += chunk;
-		file->cur_rel += chunk;
-		file->cur_ofs += chunk;
+		if (len == 1)
+		{
+			dev->write(*((uint8_t*)source));
+			file->cur_abs++;
+			file->cur_rel++;
+			file->cur_ofs++;
+			chunk = 1;
 
-		// Pointer arith!
-		source += chunk; // advance the source pointer
+		}
+		else
+		{
+			// How much can be stored in this cluster
+			chunk = MIN(fat->bs.bytes_per_cluster - file->cur_ofs, len);
+
+			dev->store(source, chunk);
+
+			// advance cursors
+			file->cur_abs += chunk;
+			file->cur_rel += chunk;
+			file->cur_ofs += chunk;
+
+			// Pointer arith!
+			source += chunk; // advance the source pointer
+		}
 
 		// detect cluster overflow
 		if (file->cur_ofs >= fat->bs.bytes_per_cluster)
@@ -662,7 +698,7 @@ bool fat16_write(FAT16_FILE* file, void* source, uint32_t len)
 
 
 /** Open next file in the directory */
-bool fat16_next(FAT16_FILE* file)
+bool ff_next(FFILE* file)
 {
 	const FAT16* fat = file->fat;
 	const BLOCKDEV* dev = fat->dev;
@@ -686,7 +722,7 @@ bool fat16_next(FAT16_FILE* file)
 
 
 /** Open previous file in the directory */
-bool fat16_prev(FAT16_FILE* file)
+bool ff_prev(FFILE* file)
 {
 	if (file->num == 0)
 		return false; // first file already
@@ -698,14 +734,14 @@ bool fat16_prev(FAT16_FILE* file)
 
 
 /** Rewind to first file in directory */
-void fat16_first(FAT16_FILE* file)
+void ff_first(FFILE* file)
 {
 	open_file(file->fat, file, file->clu, 0);
 }
 
 
 /** Open a directory denoted by the file. */
-bool fat16_opendir(FAT16_FILE* dir)
+bool ff_opendir(FFILE* dir)
 {
 	// Don't open non-dirs and "." directory.
 	if (!(dir->attribs & FA_DIR) || dir->type == FT_SELF)
@@ -716,7 +752,7 @@ bool fat16_opendir(FAT16_FILE* dir)
 }
 
 
-void fat16_root(const FAT16* fat, FAT16_FILE* file)
+void ff_root(const FAT16* fat, FFILE* file)
 {
 	open_file(fat, file, 0, 0);
 }
@@ -727,16 +763,16 @@ void fat16_root(const FAT16* fat, FAT16_FILE* file)
  * If file is found, "dir" will contain it's handle.
  * Either way, "dir" gets modified and you may need to rewind it afterwards.
  */
-bool fat16_find(FAT16_FILE* dir, const char* name)
+bool ff_open(FFILE* dir, const char* name)
 {
 	char fname[11];
-	fat16_rawname(name, fname);
+	ff_rawname(name, fname);
 	return dir_find_file_raw(dir, fname);
 }
 
 
 /** Go through a directory, and "open" first FT_NONE or FT_DELETED file entry. */
-bool find_empty_file_slot(FAT16_FILE* file)
+bool find_empty_file_slot(FFILE* file)
 {
 	const uint16_t clu = file->clu;
 	const FAT16* fat = file->fat;
@@ -781,15 +817,15 @@ bool find_empty_file_slot(FAT16_FILE* file)
 
 
 
-bool fat16_mkfile(FAT16_FILE* file, const char* name)
+bool ff_newfile(FFILE* file, const char* name)
 {
 	// Convert filename to zero padded raw string
 	char fname[11];
-	fat16_rawname(name, fname);
+	ff_rawname(name, fname);
 
 	// Abort if file already exists
 	bool exists = dir_find_file_raw(file, fname);
-	fat16_first(file); // rewind dir
+	ff_first(file); // rewind dir
 	if (exists)
 		return false; // file already exists in the dir.
 
@@ -809,15 +845,15 @@ bool fat16_mkfile(FAT16_FILE* file, const char* name)
  * Create a sub-directory of given name.
  * Directory is allocated and populated with entries "." and ".."
  */
-bool fat16_mkdir(FAT16_FILE* file, const char* name)
+bool ff_mkdir(FFILE* file, const char* name)
 {
 	// Convert filename to zero padded raw string
 	char fname[11];
-	fat16_rawname(name, fname);
+	ff_rawname(name, fname);
 
 	// Abort if file already exists
 	bool exists = dir_find_file_raw(file, fname);
-	fat16_first(file); // rewind dir
+	ff_first(file); // rewind dir
 	if (exists)
 		return false; // file already exusts in the dir.
 
@@ -840,20 +876,20 @@ bool fat16_mkdir(FAT16_FILE* file, const char* name)
 	write_file_header(file, "..         ", FA_DIR, parent_clu);
 
 	// rewind.
-	fat16_first(file);
+	ff_first(file);
 
 	return true;
 }
 
 
-char* fat16_disk_label(const FAT16* fat, char* label_out)
+char* ff_disk_label(const FAT16* fat, char* label_out)
 {
-	FAT16_FILE first;
-	fat16_root(fat, &first);
+	FFILE first;
+	ff_root(fat, &first);
 
 	if (first.type == FT_LABEL)
 	{
-		return fat16_dispname(&first, label_out);
+		return ff_dispname(&first, label_out);
 	}
 
 	// find where spaces end
@@ -876,7 +912,7 @@ char* fat16_disk_label(const FAT16* fat, char* label_out)
 }
 
 
-char* fat16_dispname(const FAT16_FILE* file, char* disp_out)
+char* ff_dispname(const FFILE* file, char* disp_out)
 {
 	// Cannot get name for special files
 	if (file->type == FT_NONE ||        // not-yet-used directory location
@@ -926,7 +962,7 @@ char* fat16_dispname(const FAT16_FILE* file, char* disp_out)
 }
 
 
-char* fat16_rawname(const char* disp_in, char* raw_out)
+char* ff_rawname(const char* disp_in, char* raw_out)
 {
 	uint8_t name_c = 0, wr_c = 0;
 	bool filling = false;
@@ -1000,21 +1036,41 @@ char* fat16_rawname(const char* disp_in, char* raw_out)
 }
 
 
-/** Write new file size (also to the disk). Does not allocate clusters. */
-void fat16_resize(FAT16_FILE* file, uint32_t size)
+FSAVEPOS ff_savepos(const FFILE* file)
+{
+	FSAVEPOS fsp;
+	fsp.clu = file->clu;
+	fsp.num = file->num;
+	fsp.cur_rel = file->cur_rel;
+	return fsp;
+}
+
+
+void ff_reopen(FFILE* file, const FSAVEPOS* pos)
+{
+	open_file(file->fat, file, pos->clu, pos->num);
+	ff_seek(file, pos->cur_rel);
+}
+
+
+void ff_flush_file(FFILE* file)
 {
 	const FAT16* fat = file->fat;
 	const BLOCKDEV* dev = file->fat->dev;
 
+	// Store open page
+	dev->flush();
+
+	// Store file size
+
 	// Find address for storing the size
 	const uint32_t addr = clu_offs(fat, file->clu, file->num * 32 + 28);
-	file->size = size;
 
 	dev->seek(addr);
-	dev->store(&size, 4);
+	dev->store(&(file->size), 4);
 
 	// Seek to the end of the file, to make sure clusters are allocated
-	fat16_seek(file, size - 1);
+	ff_seek(file, file->size - 1);
 
 	const uint16_t next = next_clu(fat, file->cur_clu);
 	if (next != 0xFFFF)
@@ -1026,8 +1082,10 @@ void fat16_resize(FAT16_FILE* file, uint32_t size)
 	}
 }
 
+
+
 /** Low level no-check file delete and free */
-void delete_file_do(FAT16_FILE* file)
+void delete_file_do(FFILE* file)
 {
 	const FAT16* fat = file->fat;
 
@@ -1049,7 +1107,7 @@ void delete_file_do(FAT16_FILE* file)
 
 
 /** Delete a simple file */
-bool fat16_rmfile(FAT16_FILE* file)
+bool ff_rmfile(FFILE* file)
 {
 	switch (file->type)
 	{
@@ -1068,19 +1126,15 @@ bool fat16_rmfile(FAT16_FILE* file)
 
 
 /** Delete an empty directory */
-bool fat16_rmdir(FAT16_FILE* file)
+bool ff_rmdir(FFILE* file)
 {
 	if (file->type != FT_SUBDIR)
 		return false; // not a subdirectory entry
 
-	const FAT16* fat = file->fat;
-
-	const uint16_t clu1 = file->clu;
-	const uint16_t num1 = file->num;
-
+	const FSAVEPOS orig = ff_savepos(file);
 
 	// Open the subdir
-	if (!fat16_opendir(file))
+	if (!ff_opendir(file))
 		return false; // could not open
 
 	// Look for valid files and subdirs in the directory
@@ -1097,16 +1151,16 @@ bool fat16_rmdir(FAT16_FILE* file)
 		{
 			// Valid child file was found, aborting.
 			// reopen original file
-			open_file(fat, file, clu1, num1);
+			ff_reopen(file, &orig);
 			return false;
 		}
 
 		if (cnt < 2) cnt++;
 	}
-	while (fat16_next(file));
+	while (ff_next(file));
 
 	// reopen original file
-	open_file(fat, file, clu1, num1);
+	ff_reopen(file, &orig);
 
 	// and delete as ordinary file
 	delete_file_do(file);
@@ -1115,7 +1169,7 @@ bool fat16_rmdir(FAT16_FILE* file)
 }
 
 
-bool fat16_delete(FAT16_FILE* file)
+bool ff_delete(FFILE* file)
 {
 	switch (file->type)
 	{
@@ -1126,8 +1180,7 @@ bool fat16_delete(FAT16_FILE* file)
 		case FT_SUBDIR:; // semicolon needed to allow declaration after "case"
 
 			// store original file location
-			const uint16_t clu1 = file->clu;
-			const uint16_t num1 = file->num;
+			const FSAVEPOS orig = ff_savepos(file);
 
 			// open the directory (skip "." and "..")
 			open_file(file->fat, file, file->clu_start, 2);
@@ -1135,34 +1188,32 @@ bool fat16_delete(FAT16_FILE* file)
 			// delete all children
 			do
 			{
-				if (!fat16_delete(file))
+				if (!ff_delete(file))
 				{
 					// failure
 					// reopen original file
-					open_file(file->fat, file, clu1, num1);
+					ff_reopen(file, &orig);
 					return false;
 				}
 			}
-			while (fat16_next(file));
+			while (ff_next(file));
 
 			// go up and delete the dir
-			open_file(file->fat, file, clu1, num1);
-			return fat16_rmdir(file);
+			ff_reopen(file, &orig);
+			return ff_rmdir(file);
 
 		default:
 			// try to delete as a regular file
-			return fat16_rmfile(file);
+			return ff_rmfile(file);
 	}
 }
 
 
-bool fat16_parent(FAT16_FILE* file)
+bool ff_parent(FFILE* file)
 {
-	const uint16_t clu1 = file->clu;
-	const uint16_t num1 = file->num;
-
 	// open second entry of the directory
 	open_file(file->fat, file, file->clu, 1);
+	const FSAVEPOS orig = ff_savepos(file);
 
 	// if it's a valid PARENT link, follow it.
 	if (file->type == FT_PARENT)
@@ -1174,7 +1225,7 @@ bool fat16_parent(FAT16_FILE* file)
 	{
 		// in root already?
 		// reopen original file
-		open_file(file->fat, file, clu1, num1);
+		ff_reopen(file, &orig);
 		return false;
 	}
 }
diff --git a/fat16.h b/fat16.h
index 477305a..149daec 100644
--- a/fat16.h
+++ b/fat16.h
@@ -1,28 +1,16 @@
 #pragma once
 
+//
+// Simple FAT16 library.
+//
+// To use it, implement BLOCKDEV functions
+// and attach them to it's instance.
+//
+
 #include <stdint.h>
 #include <stdbool.h>
 
-/**
- * Abstract block device interface
- *
- * Populate this with pointers to your I/O functions.
- */
-typedef struct
-{
-	// Sequential read
-	void (*load)(void* dest, const uint16_t len);
-	// Sequential write
-	void (*store)(const void* src, const uint16_t len);
-	// Sequential byte write
-	void (*write)(const uint8_t b);
-	// Sequential byte read
-	uint8_t (*read)(void);
-	// Absolute seek
-	void (*seek)(const uint32_t);
-	// Relative seek
-	void (*rseek)(const int16_t);
-} BLOCKDEV;
+#include "blockdev.h"
 
 
 // -------------------------------
@@ -45,6 +33,15 @@ typedef enum
 } FAT16_FT;
 
 
+/** "File address" for saving and restoring file */
+typedef struct
+{
+	uint16_t clu;
+	uint16_t num;
+	uint32_t cur_rel;
+} FSAVEPOS;
+
+
 // Include definitions of fully internal structs
 #include "fat16_internal.h"
 
@@ -102,11 +99,31 @@ typedef struct __attribute__((packed))
 	// Pointer to the FAT16 handle. (internal)
 	const FAT16* fat;
 }
-FAT16_FILE;
+FFILE;
 
 
-/** Initialize the file system - store into "fat" */
-void fat16_init(const BLOCKDEV* dev, FAT16* fat);
+/**
+ * Store modified file metadata and flush it to disk.
+ */
+void ff_flush_file(FFILE* file);
+
+
+/**
+ * Save a file "position" into a struct, for later restoration.
+ * Cursor is also saved.
+ */
+FSAVEPOS ff_savepos(const FFILE* file);
+
+/**
+ * Restore a file from a saved position.
+ */
+void ff_reopen(FFILE* file, const FSAVEPOS* pos);
+
+
+/**
+ * Initialize the file system - store into "fat"
+ */
+bool ff_init(const BLOCKDEV* dev, FAT16* fat);
 
 
 /**
@@ -114,14 +131,17 @@ void fat16_init(const BLOCKDEV* dev, FAT16* fat);
  * The file may be invalid (eg. a volume label, deleted etc),
  * or blank (type FT_NONE) if the filesystem is empty.
  */
-void fat16_root(const FAT16* fat, FAT16_FILE* file);
+void ff_root(const FAT16* fat, FFILE* file);
 
 
 /**
  * Resolve the disk label.
  * That can be in the Boot Sector, or in the first root directory entry.
+ *
+ * @param fat       the FAT handle
+ * @param label_out string to store the label in. Should have at least 12 bytes.
  */
-char* fat16_disk_label(const FAT16* fat, char* label_out);
+char* ff_disk_label(const FAT16* fat, char* label_out);
 
 
 // ----------- FILE I/O -------------
@@ -131,21 +151,26 @@ char* fat16_disk_label(const FAT16* fat, char* label_out);
  * Move file cursor to a position relative to file start
  * Returns false on I/O error (bad file, out of range...)
  */
-bool fat16_seek(FAT16_FILE* file, uint32_t addr);
+bool ff_seek(FFILE* file, uint32_t addr);
 
 
 /**
  * Read bytes from file into memory
- * Returns false on I/O error (bad file, out of range...)
+ * Returns number of bytes read, 0 on error.
  */
-bool fat16_read(FAT16_FILE* file, void* target, uint32_t len);
+uint16_t ff_read(FFILE* file, void* target, uint16_t len);
 
 
 /**
  * Write into file at a "seek" position.
- * "seek" cursor must be within (0..filesize)
  */
-bool fat16_write(FAT16_FILE* file, void* source, uint32_t len);
+bool ff_write(FFILE* file, const void* source, uint32_t len);
+
+
+/**
+ * Store a 0-terminated string at cursor.
+ */
+bool ff_write_str(FFILE* file, const char* source);
 
 
 /**
@@ -154,14 +179,14 @@ bool fat16_write(FAT16_FILE* file, void* source, uint32_t len);
  * file ... open directory; new file is opened into this handle.
  * name ... name of the new file, including extension
  */
-bool fat16_mkfile(FAT16_FILE* file, const char* name);
+bool ff_newfile(FFILE* file, const char* name);
 
 
 /**
  * Create a sub-directory of given name.
  * Directory is allocated and populated with entries "." and ".."
  */
-bool fat16_mkdir(FAT16_FILE* file, const char* name);
+bool ff_mkdir(FFILE* file, const char* name);
 
 
 /**
@@ -170,26 +195,26 @@ bool fat16_mkdir(FAT16_FILE* file, const char* name);
  *
  * Useful mainly for shrinking.
  */
-void fat16_resize(FAT16_FILE* file, uint32_t size);
+void set_file_size(FFILE* file, uint32_t size);
 
 
 /**
  * Delete a *FILE* and free it's clusters.
  */
-bool fat16_rmfile(FAT16_FILE* file);
+bool ff_rmfile(FFILE* file);
 
 
 /**
  * Delete an empty *DIRECTORY* and free it's clusters.
  */
-bool fat16_rmdir(FAT16_FILE* file);
+bool ff_rmdir(FFILE* file);
 
 
 /**
  * Delete a file or directory, even FT_LFN and FT_INVALID.
  * Directories are deleted recursively (!)
  */
-bool fat16_delete(FAT16_FILE* file);
+bool ff_delete(FFILE* file);
 
 
 
@@ -197,55 +222,55 @@ bool fat16_delete(FAT16_FILE* file);
 
 
 /** Go to previous file in the directory (false = no prev file) */
-bool fat16_prev(FAT16_FILE* file);
+bool ff_prev(FFILE* file);
 
 
 /** Go to next file in directory (false = no next file) */
-bool fat16_next(FAT16_FILE* file);
+bool ff_next(FFILE* file);
 
 
 /**
  * Open a subdirectory denoted by the file.
  * Provided handle changes to the first entry of the directory.
  */
-bool fat16_opendir(FAT16_FILE* dir);
+bool ff_opendir(FFILE* dir);
 
 
 /**
  * Open a parent directory. Fails in root.
  * Provided handle changes to the first entry of the parent directory.
  */
-bool fat16_parent(FAT16_FILE* file);
+bool ff_parent(FFILE* file);
 
 
 /** Jump to first file in this directory */
-void fat16_first(FAT16_FILE* file);
+void ff_first(FFILE* file);
 
 
 /**
- * Find a file with given "display name" in this directory.
+ * Find a file with given "display name" in this directory, and open it.
  * If file is found, "dir" will contain it's handle.
  * If file is NOT found, the handle points to the last entry of the directory.
  */
-bool fat16_find(FAT16_FILE* dir, const char* name);
+bool ff_open(FFILE* dir, const char* name);
 
 
 // -------- FILE INSPECTION -----------
 
 /** Check if file is a valid entry, or long-name/label/deleted */
-bool fat16_is_regular(const FAT16_FILE* file);
+bool ff_is_regular(const FFILE* file);
 
 
 /**
  * Resolve a file name, trim spaces and add null terminator.
  * Returns the passed char*, or NULL on error.
  */
-char* fat16_dispname(const FAT16_FILE* file, char* disp_out);
+char* ff_dispname(const FFILE* file, char* disp_out);
 
 
 /**
  * Convert filename to zero-padded fixed length one
  * Returns the passed char*.
  */
-char* fat16_rawname(const char* disp_in, char* raw_out);
+char* ff_rawname(const char* disp_in, char* raw_out);
 
diff --git a/fat16_internal.h b/fat16_internal.h
index 9f196e6..82472ac 100644
--- a/fat16_internal.h
+++ b/fat16_internal.h
@@ -9,7 +9,6 @@
 /** Boot Sector structure */
 typedef struct __attribute__((packed))
 {
-
 	// Fields loaded directly from disk:
 
 	// 13 bytes skipped
diff --git a/main.c b/main.c
index 6199434..c2df632 100644
--- a/main.c
+++ b/main.c
@@ -81,26 +81,26 @@ int main()
 
 	// Initialize the FS
 	FAT16 fat;
-	fat16_init(&test, &fat);
+	ff_init(&test, &fat);
 
-	FAT16_FILE file;
-	fat16_root(&fat, &file);
+	FFILE file;
+	ff_root(&fat, &file);
 
 	char str[12];
 
-	printf("Disk label: %s\n", fat16_disk_label(&fat, str));
+	printf("Disk label: %s\n", ff_disk_label(&fat, str));
 
 	do
 	{
-		if (!fat16_is_regular(&file))
+		if (!ff_is_regular(&file))
 			continue;
 
 		printf("File name: %s, %c, %d B, @ 0x%x\n",
-			   fat16_dispname(&file, str),
+			   ff_dispname(&file, str),
 			   file.type, file.size, file.clu_start);
 
 	}
-	while (fat16_next(&file));
+	while (ff_next(&file));
 
 //	fat16_root(&fat, &file);