#pragma once // // Simple FAT16 library. // // To use it, implement BLOCKDEV functions // and attach them to it's instance. // #include #include #include "blockdev.h" // ------------------------------- /** * File types (values can be used for debug printing). * Accessible using file->type */ typedef enum { FT_NONE = '-', FT_DELETED = 'x', FT_SUBDIR = 'D', FT_PARENT = 'P', FT_LABEL = 'L', FT_LFN = '~', FT_INVALID = '?', // not recognized weird file FT_SELF = '.', FT_FILE = 'F' } 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" /** * File handle struct. * * File handle contains cursor, file name, type, size etc. * Everything (files, dirs) is accessed using this. */ typedef struct __attribute__((packed)) { /** * Raw file name. Starting 0x05 was converted to 0xE5. * To get PRINTABLE file name, use fat16_dispname() */ uint8_t name[11]; /** * File attributes - bit field composed of FA_* flags * (internal) */ uint8_t attribs; // 14 bytes skipped (10 reserved, date, time) /** First cluster of the file. (internal) */ uint16_t clu_start; /** * File size in bytes. * This is the current allocated and readable file size. */ uint32_t size; // --- the following fields are added when reading --- /** File type. */ FAT16_FT type; // --- INTERNAL FIELDS --- // Cursor variables. (internal) uint32_t cur_abs; // absolute position in device uint32_t cur_rel; // relative position in file uint16_t cur_clu; // cluster where the cursor is uint16_t cur_ofs; // offset within the active cluster // File position in the directory. (internal) uint16_t clu; // first cluster of directory uint16_t num; // file entry number // Pointer to the FAT16 handle. (internal) const FAT16* fat; } FFILE; /** * 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); /** * Open the first file of the root directory. * The file may be invalid (eg. a volume label, deleted etc), * or blank (type FT_NONE) if the filesystem is empty. */ 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* ff_disk_label(const FAT16* fat, char* label_out); // ----------- FILE I/O ------------- /** * Move file cursor to a position relative to file start * Returns false on I/O error (bad file, out of range...) */ bool ff_seek(FFILE* file, uint32_t addr); /** * Read bytes from file into memory * Returns number of bytes read, 0 on error. */ uint16_t ff_read(FFILE* file, void* target, uint16_t len); /** * Write into file at a "seek" position. */ 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); /** * Create a new file in given folder * * file ... open directory; new file is opened into this handle. * name ... name of the new file, including extension */ bool ff_newfile(FFILE* file, const char* name); /** * Create a sub-directory of given name. * Directory is allocated and populated with entries "." and ".." */ bool ff_mkdir(FFILE* file, const char* name); /** * Set new file size. * Allocates / frees needed clusters, does NOT erase them. * * Useful mainly for shrinking. */ void set_file_size(FFILE* file, uint32_t size); /** * Delete a *FILE* and free it's clusters. */ bool ff_rmfile(FFILE* file); /** * Delete an empty *DIRECTORY* and free it's clusters. */ bool ff_rmdir(FFILE* file); /** * Delete a file or directory, even FT_LFN and FT_INVALID. * Directories are deleted recursively (!) */ bool ff_delete(FFILE* file); // --------- NAVIGATION ------------ /** Go to previous file in the directory (false = no prev file) */ bool ff_prev(FFILE* file); /** Go to next file in directory (false = no next file) */ bool ff_next(FFILE* file); /** * Open a subdirectory denoted by the file. * Provided handle changes to the first entry of the directory. */ bool ff_opendir(FFILE* dir); /** * Open a parent directory. Fails in root. * Provided handle changes to the first entry of the parent directory. */ bool ff_parent(FFILE* file); /** Jump to first file in this directory */ void ff_first(FFILE* file); /** * Find a file with given "display name" in this directory, and open it. * If file is found, "file" will contain it's handle. * Otherwise, the handle is unchanged. */ bool ff_find(FFILE* file, const char* name); // -------- FILE INSPECTION ----------- /** Check if file is a valid entry, or long-name/label/deleted */ 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* ff_dispname(const FFILE* file, char* disp_out); /** * Convert filename to zero-padded fixed length one * Returns the passed char*. */ char* ff_rawname(const char* disp_in, char* raw_out);