#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#include <fs/fat1.h>
#include <fs/fatfs_ctx.h>
#define _KERNEL_LIBK_STRING_H

#define _KERNEL_LIBK_STD_H
#define __FREESTND_C_HDRS_LIMITS_H 1
#undef CHAR_BIT
#define CHAR_BIT   __CHAR_BIT__
#define MB_LEN_MAX 1
#undef SCHAR_MAX
#define SCHAR_MAX __SCHAR_MAX__
#undef SCHAR_MIN
#define SCHAR_MIN (-SCHAR_MAX - 1)
#undef UCHAR_MAX
#define UCHAR_MAX (SCHAR_MAX * 2 + 1)
#undef CHAR_MAX
#define CHAR_MAX SCHAR_MAX
#undef CHAR_MIN
#define CHAR_MIN SCHAR_MIN
#undef SHRT_MAX
#define SHRT_MAX __SHRT_MAX__
#undef SHRT_MIN
#define SHRT_MIN (-SHRT_MAX - 1)
#undef USHRT_MAX
#define USHRT_MAX (SHRT_MAX * 2 + 1)
#undef INT_MAX
#define INT_MAX __INT_MAX__
#undef INT_MIN
#define INT_MIN (-INT_MAX - 1)
#undef UINT_MAX
#define UINT_MAX (INT_MAX * 2U + 1U)
#undef LONG_MAX
#define LONG_MAX __LONG_MAX__
#undef LONG_MIN
#define LONG_MIN (-LONG_MAX - 1L)
#undef ULONG_MAX
#define ULONG_MAX (LONG_MAX * 2UL + 1UL)
#undef LLONG_MAX
#define LLONG_MAX __LONG_LONG_MAX__
#undef LLONG_MIN
#define LLONG_MIN (-LLONG_MAX - 1LL)
#undef ULLONG_MAX
#define ULLONG_MAX                   (LLONG_MAX * 2ULL + 1ULL)
#define __FREESTND_C_HDRS_STDALIGN_H 1
#undef alignas
#define alignas _Alignas
#undef alignof
#define alignof _Alignof
#undef __alignof_is_defined
#define __alignof_is_defined 1
#undef __alignas_is_defined
#define __alignas_is_defined       1
#define __FREESTND_C_HDRS_STDARG_H 1
typedef __builtin_va_list va_list;
#undef va_start
#define va_start(v, l) __builtin_va_start (v, l)
#undef va_end
#define va_end(v) __builtin_va_end (v)
#undef va_arg
#define va_arg(v, l) __builtin_va_arg (v, l)
#undef va_copy
#define va_copy(d, s) __builtin_va_copy (d, s)

#define _STD_ATOMIC_H
#define __FREESTND_C_HDRS_STDINT_H 1
typedef __UINT8_TYPE__ uint8_t;
typedef __UINT16_TYPE__ uint16_t;
typedef __UINT32_TYPE__ uint32_t;
typedef __UINT64_TYPE__ uint64_t;
typedef __UINT_LEAST8_TYPE__ uint_least8_t;
typedef __UINT_LEAST16_TYPE__ uint_least16_t;
typedef __UINT_LEAST32_TYPE__ uint_least32_t;
typedef __UINT_LEAST64_TYPE__ uint_least64_t;
typedef __UINT_FAST8_TYPE__ uint_fast8_t;
typedef __UINT_FAST16_TYPE__ uint_fast16_t;
typedef __UINT_FAST32_TYPE__ uint_fast32_t;
typedef __UINT_FAST64_TYPE__ uint_fast64_t;
typedef __INT8_TYPE__ int8_t;
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
typedef __INT64_TYPE__ int64_t;
typedef __INT_LEAST8_TYPE__ int_least8_t;
typedef __INT_LEAST16_TYPE__ int_least16_t;
typedef __INT_LEAST32_TYPE__ int_least32_t;
typedef __INT_LEAST64_TYPE__ int_least64_t;
typedef __INT_FAST8_TYPE__ int_fast8_t;
typedef __INT_FAST16_TYPE__ int_fast16_t;
typedef __INT_FAST32_TYPE__ int_fast32_t;
typedef __INT_FAST64_TYPE__ int_fast64_t;
typedef __UINTPTR_TYPE__ uintptr_t;
typedef __INTPTR_TYPE__ intptr_t;
typedef __UINTMAX_TYPE__ uintmax_t;
typedef __INTMAX_TYPE__ intmax_t;
#define __FREESTND_C_HDRS_C_EXPAND_JOIN(x, suffix) x##suffix
#define __FREESTND_C_HDRS_C_JOIN(x, suffix)        __FREESTND_C_HDRS_C_EXPAND_JOIN (x, suffix)
#undef INT8_C
#define INT8_C(x) __FREESTND_C_HDRS_C_JOIN (x, __INT8_C_SUFFIX__)
#undef INT16_C
#define INT16_C(x) __FREESTND_C_HDRS_C_JOIN (x, __INT16_C_SUFFIX__)
#undef INT32_C
#define INT32_C(x) __FREESTND_C_HDRS_C_JOIN (x, __INT32_C_SUFFIX__)
#undef INT64_C
#define INT64_C(x) __FREESTND_C_HDRS_C_JOIN (x, __INT64_C_SUFFIX__)
#undef UINT8_C
#define UINT8_C(x) __FREESTND_C_HDRS_C_JOIN (x, __UINT8_C_SUFFIX__)
#undef UINT16_C
#define UINT16_C(x) __FREESTND_C_HDRS_C_JOIN (x, __UINT16_C_SUFFIX__)
#undef UINT32_C
#define UINT32_C(x) __FREESTND_C_HDRS_C_JOIN (x, __UINT32_C_SUFFIX__)
#undef UINT64_C
#define UINT64_C(x) __FREESTND_C_HDRS_C_JOIN (x, __UINT64_C_SUFFIX__)
#undef INTMAX_C
#define INTMAX_C(x) __FREESTND_C_HDRS_C_JOIN (x, __INTMAX_C_SUFFIX__)
#undef UINTMAX_C
#define UINTMAX_C(x) __FREESTND_C_HDRS_C_JOIN (x, __UINTMAX_C_SUFFIX__)
#undef UINT8_MAX
#define UINT8_MAX __UINT8_MAX__
#undef UINT16_MAX
#define UINT16_MAX __UINT16_MAX__
#undef UINT32_MAX
#define UINT32_MAX __UINT32_MAX__
#undef UINT64_MAX
#define UINT64_MAX __UINT64_MAX__
#undef INT8_MAX
#define INT8_MAX __INT8_MAX__
#undef INT16_MAX
#define INT16_MAX __INT16_MAX__
#undef INT32_MAX
#define INT32_MAX __INT32_MAX__
#undef INT64_MAX
#define INT64_MAX __INT64_MAX__
#undef INT8_MIN
#define INT8_MIN (-INT8_MAX - 1)
#undef INT16_MIN
#define INT16_MIN (-INT16_MAX - 1)
#undef INT32_MIN
#define INT32_MIN (-INT32_MAX - 1)
#undef INT64_MIN
#define INT64_MIN (-INT64_MAX - 1)
#undef UINT_LEAST8_MAX
#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__
#undef UINT_LEAST16_MAX
#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__
#undef UINT_LEAST32_MAX
#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__
#undef UINT_LEAST64_MAX
#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__
#undef INT_LEAST8_MAX
#define INT_LEAST8_MAX __INT_LEAST8_MAX__
#undef INT_LEAST16_MAX
#define INT_LEAST16_MAX __INT_LEAST16_MAX__
#undef INT_LEAST32_MAX
#define INT_LEAST32_MAX __INT_LEAST32_MAX__
#undef INT_LEAST64_MAX
#define INT_LEAST64_MAX __INT_LEAST64_MAX__
#undef INT_LEAST8_MIN
#define INT_LEAST8_MIN (-INT_LEAST8_MAX - 1)
#undef INT_LEAST16_MIN
#define INT_LEAST16_MIN (-INT_LEAST16_MAX - 1)
#undef INT_LEAST32_MIN
#define INT_LEAST32_MIN (-INT_LEAST32_MAX - 1)
#undef INT_LEAST64_MIN
#define INT_LEAST64_MIN (-INT_LEAST64_MAX - 1)
#undef UINT_FAST8_MAX
#define UINT_FAST8_MAX __UINT_FAST8_MAX__
#undef UINT_FAST16_MAX
#define UINT_FAST16_MAX __UINT_FAST16_MAX__
#undef UINT_FAST32_MAX
#define UINT_FAST32_MAX __UINT_FAST32_MAX__
#undef UINT_FAST64_MAX
#define UINT_FAST64_MAX __UINT_FAST64_MAX__
#undef INT_FAST8_MAX
#define INT_FAST8_MAX __INT_FAST8_MAX__
#undef INT_FAST16_MAX
#define INT_FAST16_MAX __INT_FAST16_MAX__
#undef INT_FAST32_MAX
#define INT_FAST32_MAX __INT_FAST32_MAX__
#undef INT_FAST64_MAX
#define INT_FAST64_MAX __INT_FAST64_MAX__
#undef INT_FAST8_MIN
#define INT_FAST8_MIN (-INT_FAST8_MAX - 1)
#undef INT_FAST16_MIN
#define INT_FAST16_MIN (-INT_FAST16_MAX - 1)
#undef INT_FAST32_MIN
#define INT_FAST32_MIN (-INT_FAST32_MAX - 1)
#undef INT_FAST64_MIN
#define INT_FAST64_MIN (-INT_FAST64_MAX - 1)
#undef UINTPTR_MAX
#define UINTPTR_MAX __UINTPTR_MAX__
#undef INTPTR_MAX
#define INTPTR_MAX __INTPTR_MAX__
#undef INTPTR_MIN
#define INTPTR_MIN (-INTPTR_MAX - 1)
#undef UINTMAX_MAX
#define UINTMAX_MAX __UINTMAX_MAX__
#undef INTMAX_MAX
#define INTMAX_MAX __INTMAX_MAX__
#undef INTMAX_MIN
#define INTMAX_MIN (-INTMAX_MAX - 1)
#undef PTRDIFF_MAX
#define PTRDIFF_MAX __PTRDIFF_MAX__
#undef PTRDIFF_MIN
#define PTRDIFF_MIN (-PTRDIFF_MAX - 1)
#undef SIG_ATOMIC_MAX
#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__
#undef SIG_ATOMIC_MIN
#define SIG_ATOMIC_MIN (-SIG_ATOMIC_MAX - 1)
#undef SIZE_MAX
#define SIZE_MAX __SIZE_MAX__
#undef WCHAR_MAX
#define WCHAR_MAX __WCHAR_MAX__
#undef WCHAR_MIN
#define WCHAR_MIN (-WCHAR_MAX - 1)
#undef WINT_MAX
#define WINT_MAX __WINT_MAX__
#undef WINT_MIN
#define WINT_MIN                   (-WINT_MAX - 1)
#define __FREESTND_C_HDRS_STDDEF_H 1
typedef __SIZE_TYPE__ size_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;
typedef __WCHAR_TYPE__ wchar_t;
#undef NULL
#define NULL ((void*)0)
#undef offsetof
#define offsetof(s, m) __builtin_offsetof (s, m)
typedef enum memory_order {
  memory_order_relaxed = __ATOMIC_RELAXED,
  memory_order_consume = __ATOMIC_CONSUME,
  memory_order_acquire = __ATOMIC_ACQUIRE,
  memory_order_release = __ATOMIC_RELEASE,
  memory_order_acq_rel = __ATOMIC_ACQ_REL,
  memory_order_seq_cst = __ATOMIC_SEQ_CST
} memory_order;
typedef _Atomic (_Bool) atomic_bool;
typedef _Atomic (char) atomic_char;
typedef _Atomic (signed char) atomic_schar;
typedef _Atomic (unsigned char) atomic_uchar;
typedef _Atomic (short) atomic_short;
typedef _Atomic (unsigned short) atomic_ushort;
typedef _Atomic (int) atomic_int;
typedef _Atomic (unsigned int) atomic_uint;
typedef _Atomic (long) atomic_long;
typedef _Atomic (unsigned long) atomic_ulong;
typedef _Atomic (long long) atomic_llong;
typedef _Atomic (unsigned long long) atomic_ullong;
typedef _Atomic (uint_least16_t) atomic_char16_t;
typedef _Atomic (uint_least32_t) atomic_char32_t;
typedef _Atomic (wchar_t) atomic_wchar_t;
typedef _Atomic (int_least8_t) atomic_int_least8_t;
typedef _Atomic (uint_least8_t) atomic_uint_least8_t;
typedef _Atomic (int_least16_t) atomic_int_least16_t;
typedef _Atomic (uint_least16_t) atomic_uint_least16_t;
typedef _Atomic (int_least32_t) atomic_int_least32_t;
typedef _Atomic (uint_least32_t) atomic_uint_least32_t;
typedef _Atomic (int_least64_t) atomic_int_least64_t;
typedef _Atomic (uint_least64_t) atomic_uint_least64_t;
typedef _Atomic (int_fast8_t) atomic_int_fast8_t;
typedef _Atomic (uint_fast8_t) atomic_uint_fast8_t;
typedef _Atomic (int_fast16_t) atomic_int_fast16_t;
typedef _Atomic (uint_fast16_t) atomic_uint_fast16_t;
typedef _Atomic (int_fast32_t) atomic_int_fast32_t;
typedef _Atomic (uint_fast32_t) atomic_uint_fast32_t;
typedef _Atomic (int_fast64_t) atomic_int_fast64_t;
typedef _Atomic (uint_fast64_t) atomic_uint_fast64_t;
typedef _Atomic (intptr_t) atomic_intptr_t;
typedef _Atomic (uintptr_t) atomic_uintptr_t;
typedef _Atomic (size_t) atomic_size_t;
typedef _Atomic (ptrdiff_t) atomic_ptrdiff_t;
typedef _Atomic (intmax_t) atomic_intmax_t;
typedef _Atomic (uintmax_t) atomic_uintmax_t;
typedef struct atomic_flag {
  atomic_bool _Value;
} atomic_flag;
#define ATOMIC_FLAG_INIT                 ((atomic_flag){0})
#define atomic_store(object, desired)    __c11_atomic_store (object, desired, __ATOMIC_SEQ_CST)
#define atomic_store_explicit            __c11_atomic_store
#define atomic_load(object)              __c11_atomic_load (object, __ATOMIC_SEQ_CST)
#define atomic_load_explicit             __c11_atomic_load
#define atomic_exchange(object, desired) __c11_atomic_exchange (object, desired, __ATOMIC_SEQ_CST)
#define atomic_exchange_explicit         __c11_atomic_exchange
#define atomic_compare_exchange_strong(object, expected, desired)                                  \
  __c11_atomic_compare_exchange_strong (object, expected, desired, __ATOMIC_SEQ_CST,               \
                                        __ATOMIC_SEQ_CST)
#define atomic_compare_exchange_strong_explicit __c11_atomic_compare_exchange_strong
#define atomic_compare_exchange_weak(object, expected, desired)                                    \
  __c11_atomic_compare_exchange_weak (object, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
#define atomic_compare_exchange_weak_explicit __c11_atomic_compare_exchange_weak
#define atomic_fetch_add(object, operand)     __c11_atomic_fetch_add (object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_add_explicit             __c11_atomic_fetch_add
#define atomic_fetch_sub(object, operand)     __c11_atomic_fetch_sub (object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_sub_explicit             __c11_atomic_fetch_sub
#define atomic_fetch_or(object, operand)      __c11_atomic_fetch_or (object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_or_explicit              __c11_atomic_fetch_or
#define atomic_fetch_xor(object, operand)     __c11_atomic_fetch_xor (object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_xor_explicit             __c11_atomic_fetch_xor
#define atomic_fetch_and(object, operand)     __c11_atomic_fetch_and (object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_and_explicit             __c11_atomic_fetch_and
#define atomic_flag_test_and_set(object)                                                           \
  __c11_atomic_exchange (&(object)->_Value, 1, __ATOMIC_SEQ_CST)
#define atomic_flag_test_and_set_explicit(object, order)                                           \
  __c11_atomic_exchange (&(object)->_Value, 1, order)
#define atomic_flag_clear(object)                 __c11_atomic_store (&(object)->_Value, 0, __ATOMIC_SEQ_CST)
#define atomic_flag_clear_explicit(object, order) __c11_atomic_store (&(object)->_Value, 0, order)
#define __FREESTND_C_HDRS_STDBOOL_H               1
#undef bool
#define bool _Bool
#undef true
#define true 1
#undef false
#define false 0
#undef __bool_true_false_are_defined
#define __bool_true_false_are_defined   1
#define __FREESTND_C_HDRS_STDNORETURN_H 1
#define noreturn                        _Noreturn

size_t memset (void* dst, uint8_t b, size_t n);
size_t memcpy (void* dst, const void* src, size_t n);
void strncpy (char* dst, const char* src, size_t n);
size_t strlen (const char* str);
int memcmp (const void* s1, const void* s2, size_t n);
int strncmp (const char* s1, const char* s2, size_t n);
int strcmp (const char* s1, const char* s2);
#define strlen_null(x) (strlen ((x)) + 1)

#define __FAT_MISC_H__

#define __FAT_DEFS_H__

#define __FAT_OPTS_H__
#define FATFS_IS_LITTLE_ENDIAN      1
#define FATFS_MAX_LONG_FILENAME     260
#define FATFS_MAX_OPEN_FILES        2
#define FAT_BUFFER_SECTORS          1
#define FAT_BUFFERS                 1
#define FATFS_INC_WRITE_SUPPORT     1
#define FATFS_INC_LFN_SUPPORT       1
#define FATFS_DIR_LIST_SUPPORT      1
#define FATFS_INC_TIME_DATE_SUPPORT 0
#define FATFS_INC_FORMAT_SUPPORT    1
#define FAT_SECTOR_SIZE             512

#define __FAT_TYPES_H__
#define FATFS_DEF_UINT32_AS_INT
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
#define FAT_HTONS(n) (n)
#define FAT_HTONL(n) (n)
#define STRUCT_PACK
#define STRUCT_PACK_BEGIN
#define STRUCT_PACK_END
#define STRUCT_PACKED                 __attribute__ ((packed))
#define BS_JMPBOOT                    0
#define BS_OEMNAME                    3
#define BPB_BYTSPERSEC                11
#define BPB_SECPERCLUS                13
#define BPB_RSVDSECCNT                14
#define BPB_NUMFATS                   16
#define BPB_ROOTENTCNT                17
#define BPB_TOTSEC16                  19
#define BPB_MEDIA                     21
#define BPB_FATSZ16                   22
#define BPB_SECPERTRK                 24
#define BPB_NUMHEADS                  26
#define BPB_HIDDSEC                   28
#define BPB_TOTSEC32                  32
#define BS_FAT_DRVNUM                 36
#define BS_FAT_BOOTSIG                38
#define BS_FAT_VOLID                  39
#define BS_FAT_VOLLAB                 43
#define BS_FAT_FILSYSTYPE             54
#define BPB_FAT32_FATSZ32             36
#define BPB_FAT32_EXTFLAGS            40
#define BPB_FAT32_FSVER               42
#define BPB_FAT32_ROOTCLUS            44
#define BPB_FAT32_FSINFO              48
#define BPB_FAT32_BKBOOTSEC           50
#define BS_FAT32_DRVNUM               64
#define BS_FAT32_BOOTSIG              66
#define BS_FAT32_VOLID                67
#define BS_FAT32_VOLLAB               71
#define BS_FAT32_FILSYSTYPE           82
#define FAT_TYPE_FAT12                1
#define FAT_TYPE_FAT16                2
#define FAT_TYPE_FAT32                3
#define SIGNATURE_POSITION            510
#define SIGNATURE_VALUE               0xAA55
#define PARTITION1_TYPECODE_LOCATION  450
#define FAT32_TYPECODE1               0x0B
#define FAT32_TYPECODE2               0x0C
#define PARTITION1_LBA_BEGIN_LOCATION 454
#define PARTITION1_SIZE_LOCATION      458
#define FAT_DIR_ENTRY_SIZE            32
#define FAT_SFN_SIZE_FULL             11
#define FAT_SFN_SIZE_PARTIAL          8
#define FILE_ATTR_READ_ONLY           0x01
#define FILE_ATTR_HIDDEN              0x02
#define FILE_ATTR_SYSTEM              0x04
#define FILE_ATTR_SYSHID              0x06
#define FILE_ATTR_VOLUME_ID           0x08
#define FILE_ATTR_DIRECTORY           0x10
#define FILE_ATTR_ARCHIVE             0x20
#define FILE_ATTR_LFN_TEXT            0x0F
#define FILE_HEADER_BLANK             0x00
#define FILE_HEADER_DELETED           0xE5
#define FILE_TYPE_DIR                 0x10
#define FILE_TYPE_FILE                0x20
#define FAT_TIME_HOURS_SHIFT          11
#define FAT_TIME_HOURS_MASK           0x1F
#define FAT_TIME_MINUTES_SHIFT        5
#define FAT_TIME_MINUTES_MASK         0x3F
#define FAT_TIME_SECONDS_SHIFT        0
#define FAT_TIME_SECONDS_MASK         0x1F
#define FAT_TIME_SECONDS_SCALE        2
#define FAT_DATE_YEAR_SHIFT           9
#define FAT_DATE_YEAR_MASK            0x7F
#define FAT_DATE_MONTH_SHIFT          5
#define FAT_DATE_MONTH_MASK           0xF
#define FAT_DATE_DAY_SHIFT            0
#define FAT_DATE_DAY_MASK             0x1F
#define FAT_DATE_YEAR_OFFSET          1980
#define FAT32_LAST_CLUSTER            0xFFFFFFFF
#define FAT32_INVALID_CLUSTER         0xFFFFFFFF
#define MAX_LONGFILENAME_ENTRIES      20
#define MAX_LFN_ENTRY_LENGTH          13
#define GET_32BIT_WORD(buffer, location)                                                           \
  (((uint32)buffer[location + 3] << 24) + ((uint32)buffer[location + 2] << 16) +                   \
   ((uint32)buffer[location + 1] << 8) + (uint32)buffer[location + 0])
#define GET_16BIT_WORD(buffer, location)                                                           \
  (((uint16)buffer[location + 1] << 8) + (uint16)buffer[location + 0])
#define SET_32BIT_WORD(buffer, location, value)                                                    \
  {                                                                                                \
    buffer[location + 0] = (uint8)((value) & 0xFF);                                                \
    buffer[location + 1] = (uint8)((value >> 8) & 0xFF);                                           \
    buffer[location + 2] = (uint8)((value >> 16) & 0xFF);                                          \
    buffer[location + 3] = (uint8)((value >> 24) & 0xFF);                                          \
  }
#define SET_16BIT_WORD(buffer, location, value)                                                    \
  {                                                                                                \
    buffer[location + 0] = (uint8)((value) & 0xFF);                                                \
    buffer[location + 1] = (uint8)((value >> 8) & 0xFF);                                           \
  }
void fatfs_lfn_cache_init (struct lfn_cache* lfn, int wipeTable);
void fatfs_lfn_cache_entry (struct lfn_cache* lfn, uint8* entryBuffer);
char* fatfs_lfn_cache_get (struct lfn_cache* lfn);
int fatfs_entry_lfn_text (struct fat_dir_entry* entry);
int fatfs_entry_lfn_invalid (struct fat_dir_entry* entry);
int fatfs_entry_lfn_exists (struct lfn_cache* lfn, struct fat_dir_entry* entry);
int fatfs_entry_sfn_only (struct fat_dir_entry* entry);
int fatfs_entry_is_dir (struct fat_dir_entry* entry);
int fatfs_entry_is_file (struct fat_dir_entry* entry);
int fatfs_lfn_entries_required (char* filename);
void fatfs_filename_to_lfn (char* filename, uint8* buffer, int entry, uint8 sfnChk);
void fatfs_sfn_create_entry (char* shortfilename, uint32 size, uint32 startCluster,
                             struct fat_dir_entry* entry, int dir);
int fatfs_lfn_create_sfn (char* sfn_output, char* filename);
int fatfs_lfn_generate_tail (char* sfn_output, char* sfn_input, uint32 tailNum);
void fatfs_convert_from_fat_time (uint16 fat_time, int* hours, int* minutes, int* seconds);
void fatfs_convert_from_fat_date (uint16 fat_date, int* day, int* month, int* year);
uint16 fatfs_convert_to_fat_time (int hours, int minutes, int seconds);
uint16 fatfs_convert_to_fat_date (int day, int month, int year);
void fatfs_print_sector (uint32 sector, uint8* data);
void fatfs_lfn_cache_init (struct lfn_cache* lfn, int wipeTable) {
  int i = 0;
  lfn->no_of_strings = 0;
  if (wipeTable)
    for (i = 0; i < MAX_LONGFILENAME_ENTRIES; i++)
      memset (lfn->String[i], 0x00, MAX_LFN_ENTRY_LENGTH);
}
void fatfs_lfn_cache_entry (struct lfn_cache* lfn, uint8* entryBuffer) {
  uint8 LFNIndex, i;
  LFNIndex = entryBuffer[0] & 0x1F;
  if (LFNIndex > MAX_LONGFILENAME_ENTRIES)
    return;
  if (LFNIndex == 0)
    return;
  if (lfn->no_of_strings == 0)
    lfn->no_of_strings = LFNIndex;
  lfn->String[LFNIndex - 1][0] = entryBuffer[1];
  lfn->String[LFNIndex - 1][1] = entryBuffer[3];
  lfn->String[LFNIndex - 1][2] = entryBuffer[5];
  lfn->String[LFNIndex - 1][3] = entryBuffer[7];
  lfn->String[LFNIndex - 1][4] = entryBuffer[9];
  lfn->String[LFNIndex - 1][5] = entryBuffer[0x0E];
  lfn->String[LFNIndex - 1][6] = entryBuffer[0x10];
  lfn->String[LFNIndex - 1][7] = entryBuffer[0x12];
  lfn->String[LFNIndex - 1][8] = entryBuffer[0x14];
  lfn->String[LFNIndex - 1][9] = entryBuffer[0x16];
  lfn->String[LFNIndex - 1][10] = entryBuffer[0x18];
  lfn->String[LFNIndex - 1][11] = entryBuffer[0x1C];
  lfn->String[LFNIndex - 1][12] = entryBuffer[0x1E];
  for (i = 0; i < MAX_LFN_ENTRY_LENGTH; i++)
    if (lfn->String[LFNIndex - 1][i] == 0xFF)
      lfn->String[LFNIndex - 1][i] = 0x20;
}
char* fatfs_lfn_cache_get (struct lfn_cache* lfn) {
  if (lfn->no_of_strings == MAX_LONGFILENAME_ENTRIES)
    lfn->Null = '\0';
  else if (lfn->no_of_strings)
    lfn->String[lfn->no_of_strings][0] = '\0';
  else
    lfn->String[0][0] = '\0';
  return (char*)&lfn->String[0][0];
}
int fatfs_entry_lfn_text (struct fat_dir_entry* entry) {
  if ((entry->Attr & FILE_ATTR_LFN_TEXT) == FILE_ATTR_LFN_TEXT)
    return 1;
  else
    return 0;
}
int fatfs_entry_lfn_invalid (struct fat_dir_entry* entry) {
  if ((entry->Name[0] == FILE_HEADER_BLANK) || (entry->Name[0] == FILE_HEADER_DELETED) ||
      (entry->Attr == FILE_ATTR_VOLUME_ID) || (entry->Attr & FILE_ATTR_SYSHID))
    return 1;
  else
    return 0;
}
int fatfs_entry_lfn_exists (struct lfn_cache* lfn, struct fat_dir_entry* entry) {
  if ((entry->Attr != FILE_ATTR_LFN_TEXT) && (entry->Name[0] != FILE_HEADER_BLANK) &&
      (entry->Name[0] != FILE_HEADER_DELETED) && (entry->Attr != FILE_ATTR_VOLUME_ID) &&
      (!(entry->Attr & FILE_ATTR_SYSHID)) && (lfn->no_of_strings))
    return 1;
  else
    return 0;
}
int fatfs_entry_sfn_only (struct fat_dir_entry* entry) {
  if ((entry->Attr != FILE_ATTR_LFN_TEXT) && (entry->Name[0] != FILE_HEADER_BLANK) &&
      (entry->Name[0] != FILE_HEADER_DELETED) && (entry->Attr != FILE_ATTR_VOLUME_ID) &&
      (!(entry->Attr & FILE_ATTR_SYSHID)))
    return 1;
  else
    return 0;
}
int fatfs_entry_is_dir (struct fat_dir_entry* entry) {
  if (entry->Attr & FILE_TYPE_DIR)
    return 1;
  else
    return 0;
}
int fatfs_entry_is_file (struct fat_dir_entry* entry) {
  if (entry->Attr & FILE_TYPE_FILE)
    return 1;
  else
    return 0;
}
int fatfs_lfn_entries_required (char* filename) {
  int length = (int)strlen (filename);
  if (length)
    return (length + MAX_LFN_ENTRY_LENGTH - 1) / MAX_LFN_ENTRY_LENGTH;
  else
    return 0;
}
void fatfs_filename_to_lfn (char* filename, uint8* buffer, int entry, uint8 sfnChk) {
  int i;
  int nameIndexes[MAX_LFN_ENTRY_LENGTH] = {1,    3,    5,    7,    9,    0x0E, 0x10,
                                           0x12, 0x14, 0x16, 0x18, 0x1C, 0x1E};
  int length = (int)strlen (filename);
  int entriesRequired = fatfs_lfn_entries_required (filename);
  int start = entry * MAX_LFN_ENTRY_LENGTH;
  memset (buffer, 0x00, FAT_DIR_ENTRY_SIZE);
  buffer[0] = (uint8)(((entriesRequired - 1) == entry) ? (0x40 | (entry + 1)) : (entry + 1));
  buffer[11] = 0x0F;
  buffer[13] = sfnChk;
  for (i = 0; i < MAX_LFN_ENTRY_LENGTH; i++) {
    if ((start + i) < length)
      buffer[nameIndexes[i]] = filename[start + i];
    else if ((start + i) == length)
      buffer[nameIndexes[i]] = 0x00;
    else {
      buffer[nameIndexes[i]] = 0xFF;
      buffer[nameIndexes[i] + 1] = 0xFF;
    }
  }
}
void fatfs_sfn_create_entry (char* shortfilename, uint32 size, uint32 startCluster,
                             struct fat_dir_entry* entry, int dir) {
  int i;
  for (i = 0; i < FAT_SFN_SIZE_FULL; i++)
    entry->Name[i] = shortfilename[i];
  entry->CrtTimeTenth = 0x00;
  entry->CrtTime[1] = entry->CrtTime[0] = 0x00;
  entry->CrtDate[1] = 0x00;
  entry->CrtDate[0] = 0x20;
  entry->LstAccDate[1] = 0x00;
  entry->LstAccDate[0] = 0x20;
  entry->WrtTime[1] = entry->WrtTime[0] = 0x00;
  entry->WrtDate[1] = 0x00;
  entry->WrtDate[0] = 0x20;
  if (!dir)
    entry->Attr = FILE_TYPE_FILE;
  else
    entry->Attr = FILE_TYPE_DIR;
  entry->NTRes = 0x00;
  entry->FstClusHI = FAT_HTONS ((uint16)((startCluster >> 16) & 0xFFFF));
  entry->FstClusLO = FAT_HTONS ((uint16)((startCluster >> 0) & 0xFFFF));
  entry->FileSize = FAT_HTONL (size);
}
int fatfs_lfn_create_sfn (char* sfn_output, char* filename) {
  int i;
  int dotPos = -1;
  char ext[3];
  int pos;
  int len = (int)strlen (filename);
  if (filename[0] == '.')
    return 0;
  memset (sfn_output, ' ', FAT_SFN_SIZE_FULL);
  memset (ext, ' ', 3);
  for (i = 0; i < len; i++) {
    if (filename[i] == '.')
      dotPos = i;
  }
  if (dotPos != -1) {
    for (i = (dotPos + 1); i < (dotPos + 1 + 3); i++)
      if (i < len)
        ext[i - (dotPos + 1)] = filename[i];
    len = dotPos;
  }
  pos = 0;
  for (i = 0; i < len; i++) {
    if ((filename[i] != ' ') && (filename[i] != '.')) {
      if (filename[i] >= 'a' && filename[i] <= 'z')
        sfn_output[pos++] = filename[i] - 'a' + 'A';
      else
        sfn_output[pos++] = filename[i];
    }
    if (pos == FAT_SFN_SIZE_PARTIAL)
      break;
  }
  for (i = FAT_SFN_SIZE_PARTIAL; i < FAT_SFN_SIZE_FULL; i++) {
    if (ext[i - FAT_SFN_SIZE_PARTIAL] >= 'a' && ext[i - FAT_SFN_SIZE_PARTIAL] <= 'z')
      sfn_output[i] = ext[i - FAT_SFN_SIZE_PARTIAL] - 'a' + 'A';
    else
      sfn_output[i] = ext[i - FAT_SFN_SIZE_PARTIAL];
  }
  return 1;
}
static void fatfs_itoa (uint32 num, char* s) {
  char* cp;
  char outbuf[12];
  const char digits[] = "0123456789ABCDEF";
  cp = outbuf;
  do {
    *cp++ = digits[(int)(num % 10)];
  } while ((num /= 10) > 0);
  *cp-- = 0;
  while (cp >= outbuf)
    *s++ = *cp--;
  *s = 0;
}
int fatfs_lfn_generate_tail (char* sfn_output, char* sfn_input, uint32 tailNum) {
  int tail_chars;
  char tail_str[12];
  if (tailNum > 99999)
    return 0;
  memset (tail_str, 0x00, sizeof (tail_str));
  tail_str[0] = '~';
  fatfs_itoa (tailNum, tail_str + 1);
  memcpy (sfn_output, sfn_input, FAT_SFN_SIZE_FULL);
  tail_chars = (int)strlen (tail_str);
  memcpy (sfn_output + (FAT_SFN_SIZE_PARTIAL - tail_chars), tail_str, tail_chars);
  return 1;
}

#define __FAT_CACHE_H__

#define __FAT_FILELIB_H__

#define __FAT_ACCESS_H__
#define FAT_INIT_OK                   0
#define FAT_INIT_MEDIA_ACCESS_ERROR   (-1)
#define FAT_INIT_INVALID_SECTOR_SIZE  (-2)
#define FAT_INIT_INVALID_SIGNATURE    (-3)
#define FAT_INIT_ENDIAN_ERROR         (-4)
#define FAT_INIT_WRONG_FILESYS_TYPE   (-5)
#define FAT_INIT_WRONG_PARTITION_TYPE (-6)
#define FAT_INIT_STRUCT_PACKING       (-7)
#define FAT_DIR_ENTRIES_PER_SECTOR    (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
typedef int (*fn_diskio_read) (struct fatfs_ctx* ctx, uint32 sector, uint8* buffer,
                               uint32 sector_count);
typedef int (*fn_diskio_write) (struct fatfs_ctx* ctx, uint32 sector, uint8* buffer,
                                uint32 sector_count);
int fatfs_init (struct fatfs_ctx* ctx, struct fatfs* fs);
uint32 fatfs_lba_of_cluster (struct fatfs* fs, uint32 Cluster_Number);
int fatfs_sector_reader (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 Startcluster,
                         uint32 offset, uint8* target);
int fatfs_sector_read (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 lba, uint8* target,
                       uint32 count);
int fatfs_sector_write (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 lba, uint8* target,
                        uint32 count);
int fatfs_read_sector (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 cluster, uint32 sector,
                       uint8* target);
int fatfs_write_sector (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 cluster, uint32 sector,
                        uint8* target);
void fatfs_show_details (struct fatfs* fs);
uint32 fatfs_get_root_cluster (struct fatfs* fs);
uint32 fatfs_get_file_entry (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 Cluster,
                             char* nametofind, struct fat_dir_entry* sfEntry);
int fatfs_sfn_exists (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 Cluster, char* shortname);
int fatfs_update_file_length (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 Cluster,
                              char* shortname, uint32 fileLength);
int fatfs_mark_file_deleted (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 Cluster,
                             char* shortname);
void fatfs_list_directory_start (struct fatfs* fs, struct fs_dir_list_status* dirls,
                                 uint32 StartCluster);
int fatfs_list_directory_next (struct fatfs_ctx* ctx, struct fatfs* fs,
                               struct fs_dir_list_status* dirls, struct fs_dir_ent* entry);
int fatfs_update_timestamps (struct fat_dir_entry* directoryEntry, int create, int modify,
                             int access);

#define __FAT_LIST_H__
#define FAT_ASSERT(x)
#define FAT_INLINE
struct fat_list;
#define fat_list_entry(p, t, m) p ? ((t*)((char*)(p) - (char*)(&((t*)0)->m))) : 0
#define fat_list_next(l, p)     (p)->next
#define fat_list_prev(l, p)     (p)->previous
#define fat_list_first(l)       (l)->head
#define fat_list_last(l)        (l)->tail
#define fat_list_for_each(l, p) for ((p) = (l)->head; (p); (p) = (p)->next)
#define SEEK_CUR                1
#define SEEK_END                2
#define SEEK_SET                0
#define EOF                     (-1)
struct sFL_FILE;
void fl_init (struct fatfs_ctx* ctx);
void fl_attach_locks (struct fatfs_ctx* ctx, void (*lock) (void), void (*unlock) (void));
int fl_attach_media (struct fatfs_ctx* ctx, fn_diskio_read rd, fn_diskio_write wr);
void fl_shutdown (struct fatfs_ctx* ctx);
void* fl_fopen (struct fatfs_ctx* ctx, const char* path, const char* modifiers);
void fl_fclose (struct fatfs_ctx* ctx, void* file);
int fl_fflush (struct fatfs_ctx* ctx, void* file);
int fl_fgetc (struct fatfs_ctx* ctx, void* file);
char* fl_fgets (struct fatfs_ctx* ctx, char* s, int n, void* f);
int fl_fputc (struct fatfs_ctx* ctx, int c, void* file);
int fl_fputs (struct fatfs_ctx* ctx, const char* str, void* file);
int fl_fwrite (struct fatfs_ctx* ctx, const void* data, int size, int count, void* file);
int fl_fread (struct fatfs_ctx* ctx, void* data, int size, int count, void* file);
int fl_fseek (struct fatfs_ctx* ctx, void* file, long offset, int origin);
int fl_fgetpos (struct fatfs_ctx* ctx, void* file, uint32* position);
long fl_ftell (struct fatfs_ctx* ctx, void* f);
int fl_feof (struct fatfs_ctx* ctx, void* f);
int fl_remove (struct fatfs_ctx* ctx, const char* filename);
typedef struct fs_dir_list_status FL_DIR;
typedef struct fs_dir_ent fl_dirent;
FL_DIR* fl_opendir (struct fatfs_ctx* ctx, const char* path, FL_DIR* dir);
int fl_readdir (struct fatfs_ctx* ctx, FL_DIR* dirls, fl_dirent* entry);
int fl_closedir (struct fatfs_ctx* ctx, FL_DIR* dir);
void fl_listdirectory (struct fatfs_ctx* ctx, const char* path);
int fl_createdirectory (struct fatfs_ctx* ctx, const char* path);
int fl_is_dir (struct fatfs_ctx* ctx, const char* path);
int fl_format (struct fatfs_ctx* ctx, uint32 volume_sectors, const char* name);
int fatfs_cache_init (struct fatfs* fs, FL_FILE* file);
int fatfs_cache_get_next_cluster (struct fatfs* fs, FL_FILE* file, uint32 clusterIdx,
                                  uint32* pNextCluster);
int fatfs_cache_set_next_cluster (struct fatfs* fs, FL_FILE* file, uint32 clusterIdx,
                                  uint32 nextCluster);
int fatfs_cache_init (struct fatfs* fs, FL_FILE* file) { return 1; }
int fatfs_cache_get_next_cluster (struct fatfs* fs, FL_FILE* file, uint32 clusterIdx,
                                  uint32* pNextCluster) {
  return 0;
}
int fatfs_cache_set_next_cluster (struct fatfs* fs, FL_FILE* file, uint32 clusterIdx,
                                  uint32 nextCluster) {
  return 1;
}

#define __FAT_TABLE_H__
void fatfs_fat_init (struct fatfs* fs);
int fatfs_fat_purge (struct fatfs_ctx* ctx, struct fatfs* fs);
uint32 fatfs_find_next_cluster (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 current_cluster);
void fatfs_set_fs_info_next_free_cluster (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 newValue);
int fatfs_find_blank_cluster (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 start_cluster,
                              uint32* free_cluster);
int fatfs_fat_set_cluster (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 cluster,
                           uint32 next_cluster);
int fatfs_fat_add_cluster_to_chain (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 start_cluster,
                                    uint32 newEntry);
int fatfs_free_cluster_chain (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 start_cluster);
uint32 fatfs_count_free_clusters (struct fatfs_ctx* ctx, struct fatfs* fs);
#define FAT32_GET_32BIT_WORD(pbuf, location) (GET_32BIT_WORD (pbuf->ptr, location))
#define FAT32_SET_32BIT_WORD(pbuf, location, value)                                                \
  {                                                                                                \
    SET_32BIT_WORD (pbuf->ptr, location, value);                                                   \
    pbuf->dirty = 1;                                                                               \
  }
#define FAT16_GET_16BIT_WORD(pbuf, location) (GET_16BIT_WORD (pbuf->ptr, location))
#define FAT16_SET_16BIT_WORD(pbuf, location, value)                                                \
  {                                                                                                \
    SET_16BIT_WORD (pbuf->ptr, location, value);                                                   \
    pbuf->dirty = 1;                                                                               \
  }
void fatfs_fat_init (struct fatfs* fs) {
  int i;
  fs->fat_buffer_head = NULL;
  for (i = 0; i < FAT_BUFFERS; i++) {
    fs->fat_buffers[i].address = FAT32_INVALID_CLUSTER;
    fs->fat_buffers[i].dirty = 0;
    memset (fs->fat_buffers[i].sector, 0x00, sizeof (fs->fat_buffers[i].sector));
    fs->fat_buffers[i].ptr = NULL;
    fs->fat_buffers[i].next = fs->fat_buffer_head;
    fs->fat_buffer_head = &fs->fat_buffers[i];
  }
}
static int fatfs_fat_writeback (struct fatfs_ctx* ctx, struct fatfs* fs, struct fat_buffer* pcur) {
  if (pcur) {
    if (pcur->dirty) {
      if (fs->disk_io.write_media) {
        uint32 sectors = FAT_BUFFER_SECTORS;
        uint32 offset = pcur->address - fs->fat_begin_lba;
        if ((offset + FAT_BUFFER_SECTORS) <= fs->fat_sectors)
          sectors = FAT_BUFFER_SECTORS;
        else
          sectors = fs->fat_sectors - offset;
        if (!fs->disk_io.write_media (ctx, pcur->address, pcur->sector, sectors))
          return 0;
      }
      pcur->dirty = 0;
    }
    return 1;
  } else
    return 0;
}
static struct fat_buffer* fatfs_fat_read_sector (struct fatfs_ctx* ctx, struct fatfs* fs,
                                                 uint32 sector) {
  struct fat_buffer* last = NULL;
  struct fat_buffer* pcur = fs->fat_buffer_head;
  while (pcur) {
    if ((sector >= pcur->address) && (sector < (pcur->address + FAT_BUFFER_SECTORS)))
      break;
    if (pcur->next == NULL) {
      if (last)
        last->next = NULL;
      else
        fs->fat_buffer_head = NULL;
    }
    last = pcur;
    pcur = pcur->next;
  }
  if (pcur) {
    pcur->ptr = (uint8*)(pcur->sector + ((sector - pcur->address) * FAT_SECTOR_SIZE));
    return pcur;
  }
  pcur = last;
  pcur->next = fs->fat_buffer_head;
  fs->fat_buffer_head = pcur;
  if (pcur->dirty)
    if (!fatfs_fat_writeback (ctx, fs, pcur))
      return 0;
  pcur->address = sector;
  if (!fs->disk_io.read_media (ctx, pcur->address, pcur->sector, FAT_BUFFER_SECTORS)) {
    pcur->address = FAT32_INVALID_CLUSTER;
    return NULL;
  }
  pcur->ptr = pcur->sector;
  return pcur;
}
int fatfs_fat_purge (struct fatfs_ctx* ctx, struct fatfs* fs) {
  struct fat_buffer* pcur = fs->fat_buffer_head;
  while (pcur) {
    if (pcur->dirty)
      if (!fatfs_fat_writeback (ctx, fs, pcur))
        return 0;
    pcur = pcur->next;
  }
  return 1;
}
uint32 fatfs_find_next_cluster (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 current_cluster) {
  uint32 fat_sector_offset, position;
  uint32 nextcluster;
  struct fat_buffer* pbuf;
  if (current_cluster == 0)
    current_cluster = 2;
  if (fs->fat_type == FAT_TYPE_16)
    fat_sector_offset = current_cluster / 256;
  else
    fat_sector_offset = current_cluster / 128;
  pbuf = fatfs_fat_read_sector (ctx, fs, fs->fat_begin_lba + fat_sector_offset);
  if (!pbuf)
    return (FAT32_LAST_CLUSTER);
  if (fs->fat_type == FAT_TYPE_16) {
    position = (current_cluster - (fat_sector_offset * 256)) * 2;
    nextcluster = FAT16_GET_16BIT_WORD (pbuf, (uint16)position);
    if (nextcluster >= 0xFFF8 && nextcluster <= 0xFFFF)
      return (FAT32_LAST_CLUSTER);
  } else {
    position = (current_cluster - (fat_sector_offset * 128)) * 4;
    nextcluster = FAT32_GET_32BIT_WORD (pbuf, (uint16)position);
    nextcluster = nextcluster & 0x0FFFFFFF;
    if (nextcluster >= 0x0FFFFFF8 && nextcluster <= 0x0FFFFFFF)
      return (FAT32_LAST_CLUSTER);
  }
  return (nextcluster);
}
void fatfs_set_fs_info_next_free_cluster (struct fatfs_ctx* ctx, struct fatfs* fs,
                                          uint32 newValue) {
  if (fs->fat_type == FAT_TYPE_16)
    ;
  else {
    struct fat_buffer* pbuf = fatfs_fat_read_sector (ctx, fs, fs->lba_begin + fs->fs_info_sector);
    if (!pbuf)
      return;
    FAT32_SET_32BIT_WORD (pbuf, 492, newValue);
    fs->next_free_cluster = newValue;
    if (fs->disk_io.write_media)
      fs->disk_io.write_media (ctx, pbuf->address, pbuf->sector, 1);
    pbuf->address = FAT32_INVALID_CLUSTER;
    pbuf->dirty = 0;
  }
}
int fatfs_find_blank_cluster (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 start_cluster,
                              uint32* free_cluster) {
  uint32 fat_sector_offset, position;
  uint32 nextcluster;
  uint32 current_cluster = start_cluster;
  struct fat_buffer* pbuf;
  do {
    if (fs->fat_type == FAT_TYPE_16)
      fat_sector_offset = current_cluster / 256;
    else
      fat_sector_offset = current_cluster / 128;
    if (fat_sector_offset < fs->fat_sectors) {
      pbuf = fatfs_fat_read_sector (ctx, fs, fs->fat_begin_lba + fat_sector_offset);
      if (!pbuf)
        return 0;
      if (fs->fat_type == FAT_TYPE_16) {
        position = (current_cluster - (fat_sector_offset * 256)) * 2;
        nextcluster = FAT16_GET_16BIT_WORD (pbuf, (uint16)position);
      } else {
        position = (current_cluster - (fat_sector_offset * 128)) * 4;
        nextcluster = FAT32_GET_32BIT_WORD (pbuf, (uint16)position);
        nextcluster = nextcluster & 0x0FFFFFFF;
      }
      if (nextcluster != 0)
        current_cluster++;
    } else
      return 0;
  } while (nextcluster != 0x0);
  *free_cluster = current_cluster;
  return 1;
}
int fatfs_fat_set_cluster (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 cluster,
                           uint32 next_cluster) {
  struct fat_buffer* pbuf;
  uint32 fat_sector_offset, position;
  if (fs->fat_type == FAT_TYPE_16)
    fat_sector_offset = cluster / 256;
  else
    fat_sector_offset = cluster / 128;
  pbuf = fatfs_fat_read_sector (ctx, fs, fs->fat_begin_lba + fat_sector_offset);
  if (!pbuf)
    return 0;
  if (fs->fat_type == FAT_TYPE_16) {
    position = (cluster - (fat_sector_offset * 256)) * 2;
    FAT16_SET_16BIT_WORD (pbuf, (uint16)position, ((uint16)next_cluster));
  } else {
    position = (cluster - (fat_sector_offset * 128)) * 4;
    FAT32_SET_32BIT_WORD (pbuf, (uint16)position, next_cluster);
  }
  return 1;
}
int fatfs_free_cluster_chain (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 start_cluster) {
  uint32 last_cluster;
  uint32 next_cluster = start_cluster;
  while ((next_cluster != FAT32_LAST_CLUSTER) && (next_cluster != 0x00000000)) {
    last_cluster = next_cluster;
    next_cluster = fatfs_find_next_cluster (ctx, fs, next_cluster);
    fatfs_fat_set_cluster (ctx, fs, last_cluster, 0x00000000);
  }
  return 1;
}
int fatfs_fat_add_cluster_to_chain (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 start_cluster,
                                    uint32 newEntry) {
  uint32 last_cluster = FAT32_LAST_CLUSTER;
  uint32 next_cluster = start_cluster;
  if (start_cluster == FAT32_LAST_CLUSTER)
    return 0;
  while (next_cluster != FAT32_LAST_CLUSTER) {
    last_cluster = next_cluster;
    next_cluster = fatfs_find_next_cluster (ctx, fs, next_cluster);
    if (!next_cluster)
      return 0;
  }
  fatfs_fat_set_cluster (ctx, fs, last_cluster, newEntry);
  fatfs_fat_set_cluster (ctx, fs, newEntry, FAT32_LAST_CLUSTER);
  return 1;
}
uint32 fatfs_count_free_clusters (struct fatfs_ctx* ctx, struct fatfs* fs) {
  uint32 i, j;
  uint32 count = 0;
  struct fat_buffer* pbuf;
  for (i = 0; i < fs->fat_sectors; i++) {
    pbuf = fatfs_fat_read_sector (ctx, fs, fs->fat_begin_lba + i);
    if (!pbuf)
      break;
    for (j = 0; j < FAT_SECTOR_SIZE;) {
      if (fs->fat_type == FAT_TYPE_16) {
        if (FAT16_GET_16BIT_WORD (pbuf, (uint16)j) == 0)
          count++;
        j += 2;
      } else {
        if (FAT32_GET_32BIT_WORD (pbuf, (uint16)j) == 0)
          count++;
        j += 4;
      }
    }
  }
  return count;
}

#define __FAT_WRITE_H__
int fatfs_add_file_entry (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 dirCluster,
                          char* filename, char* shortfilename, uint32 startCluster, uint32 size,
                          int dir);
int fatfs_add_free_space (struct fatfs_ctx* ctx, struct fatfs* fs, uint32* startCluster,
                          uint32 clusters);
int fatfs_allocate_free_space (struct fatfs_ctx* ctx, struct fatfs* fs, int newFile,
                               uint32* startCluster, uint32 size);

#define __FILESTRING_H__
int fatfs_total_path_levels (char* path);
int fatfs_get_substring (char* Path, int levelreq, char* output, int max_len);
int fatfs_split_path (char* FullPath, char* Path, int max_path, char* FileName, int max_filename);
int fatfs_compare_names (char* strA, char* strB);
int fatfs_string_ends_with_slash (char* path);
int fatfs_get_sfn_display_name (char* out, char* in);
int fatfs_get_extension (char* filename, char* out, int maxlen);
int fatfs_create_path_string (char* path, char* filename, char* out, int maxlen);
int fatfs_add_free_space (struct fatfs_ctx* ctx, struct fatfs* fs, uint32* startCluster,
                          uint32 clusters) {
  uint32 i;
  uint32 nextcluster;
  uint32 start = *startCluster;
  if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
    fatfs_set_fs_info_next_free_cluster (ctx, fs, FAT32_LAST_CLUSTER);
  for (i = 0; i < clusters; i++) {
    if (fatfs_find_blank_cluster (ctx, fs, fs->rootdir_first_cluster, &nextcluster)) {
      fatfs_fat_set_cluster (ctx, fs, start, nextcluster);
      fatfs_fat_set_cluster (ctx, fs, nextcluster, FAT32_LAST_CLUSTER);
      start = nextcluster;
      if (i == 0)
        *startCluster = nextcluster;
    } else
      return 0;
  }
  return 1;
}
int fatfs_allocate_free_space (struct fatfs_ctx* ctx, struct fatfs* fs, int newFile,
                               uint32* startCluster, uint32 size) {
  uint32 clusterSize;
  uint32 clusterCount;
  uint32 nextcluster;
  if (size == 0)
    return 0;
  if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
    fatfs_set_fs_info_next_free_cluster (ctx, fs, FAT32_LAST_CLUSTER);
  clusterSize = fs->sectors_per_cluster * FAT_SECTOR_SIZE;
  clusterCount = (size / clusterSize);
  if (size - (clusterSize * clusterCount))
    clusterCount++;
  if (newFile) {
    if (!fatfs_find_blank_cluster (ctx, fs, fs->rootdir_first_cluster, &nextcluster))
      return 0;
    if (clusterCount == 1) {
      fatfs_fat_set_cluster (ctx, fs, nextcluster, FAT32_LAST_CLUSTER);
      *startCluster = nextcluster;
      return 1;
    }
  } else
    nextcluster = *startCluster;
  if (!fatfs_add_free_space (ctx, fs, &nextcluster, clusterCount))
    return 0;
  return 1;
}
static int fatfs_find_free_dir_offset (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 dirCluster,
                                       int entryCount, uint32* pSector, uint8* pOffset) {
  struct fat_dir_entry* directoryEntry;
  uint8 item = 0;
  uint16 recordoffset = 0;
  uint8 i = 0;
  int x = 0;
  int possible_spaces = 0;
  int start_recorded = 0;
  if (entryCount == 0)
    return 0;
  while (1) {
    if (fatfs_sector_reader (ctx, fs, dirCluster, x++, 0)) {
      for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++) {
        recordoffset = FAT_DIR_ENTRY_SIZE * item;
        directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector + recordoffset);
        if (fatfs_entry_lfn_text (directoryEntry)) {
          if (possible_spaces == 0) {
            *pSector = x - 1;
            *pOffset = item;
            start_recorded = 1;
          }
          possible_spaces++;
        } else {
          if (fs->currentsector.sector[recordoffset] == FILE_HEADER_DELETED) {
            if (possible_spaces == 0) {
              *pSector = x - 1;
              *pOffset = item;
              start_recorded = 1;
            }
            possible_spaces++;
            if (possible_spaces >= entryCount)
              return 1;
          } else if (fs->currentsector.sector[recordoffset] == FILE_HEADER_BLANK) {
            if (possible_spaces == 0) {
              *pSector = x - 1;
              *pOffset = item;
              start_recorded = 1;
            }
            possible_spaces++;
            if (possible_spaces >= entryCount)
              return 1;
          } else {
            possible_spaces = 0;
            start_recorded = 0;
          }
        }
      }
    } else {
      uint32 newCluster;
      if (!fatfs_find_blank_cluster (ctx, fs, fs->rootdir_first_cluster, &newCluster))
        return 0;
      if (!fatfs_fat_add_cluster_to_chain (ctx, fs, dirCluster, newCluster))
        return 0;
      memset (fs->currentsector.sector, 0x00, FAT_SECTOR_SIZE);
      for (i = 0; i < fs->sectors_per_cluster; i++) {
        if (!fatfs_write_sector (ctx, fs, newCluster, i, 0))
          return 0;
      }
      if (!start_recorded) {
        *pSector = (x - 1);
        *pOffset = 0;
        start_recorded = 1;
      }
      return 1;
    }
  }
  return 0;
}
int fatfs_add_file_entry (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 dirCluster,
                          char* filename, char* shortfilename, uint32 startCluster, uint32 size,
                          int dir) {
  uint8 item = 0;
  uint16 recordoffset = 0;
  uint8 i = 0;
  uint32 x = 0;
  int entryCount;
  struct fat_dir_entry shortEntry;
  int dirtySector = 0;
  uint32 dirSector = 0;
  uint8 dirOffset = 0;
  int foundEnd = 0;
  uint8 checksum;
  uint8* pSname;
  if (!fs->disk_io.write_media)
    return 0;
  entryCount = fatfs_lfn_entries_required (filename);
  if (!entryCount)
    return 0;
  if (!fatfs_find_free_dir_offset (ctx, fs, dirCluster, entryCount + 1, &dirSector, &dirOffset))
    return 0;
  pSname = (uint8*)shortfilename;
  checksum = 0;
  for (i = 11; i != 0; i--)
    checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *pSname++;
  x = dirSector;
  while (1) {
    if (fatfs_sector_reader (ctx, fs, dirCluster, x++, 0)) {
      for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++) {
        recordoffset = FAT_DIR_ENTRY_SIZE * item;
        if (foundEnd == 0)
          if ((dirSector == (x - 1)) && (dirOffset == item))
            foundEnd = 1;
        if (foundEnd) {
          if (entryCount == 0) {
            fatfs_sfn_create_entry (shortfilename, size, startCluster, &shortEntry, dir);
            memcpy (&fs->currentsector.sector[recordoffset], &shortEntry, sizeof (shortEntry));
            return fs->disk_io.write_media (ctx, fs->currentsector.address,
                                            fs->currentsector.sector, 1);
          } else {
            entryCount--;
            fatfs_filename_to_lfn (filename, &fs->currentsector.sector[recordoffset], entryCount,
                                   checksum);
            dirtySector = 1;
          }
        }
      }
      if (dirtySector) {
        if (!fs->disk_io.write_media (ctx, fs->currentsector.address, fs->currentsector.sector, 1))
          return 0;
        dirtySector = 0;
      }
    } else
      return 0;
  }
  return 0;
}
int fatfs_init (struct fatfs_ctx* ctx, struct fatfs* fs) {
  uint8 num_of_fats;
  uint16 reserved_sectors;
  uint32 FATSz;
  uint32 root_dir_sectors;
  uint32 total_sectors;
  uint32 data_sectors;
  uint32 count_of_clusters;
  uint8 valid_partition = 0;
  fs->currentsector.address = FAT32_INVALID_CLUSTER;
  fs->currentsector.dirty = 0;
  fs->next_free_cluster = 0;
  fatfs_fat_init (fs);
  if (!fs->disk_io.read_media)
    return FAT_INIT_MEDIA_ACCESS_ERROR;
  if (!fs->disk_io.read_media (ctx, 0, fs->currentsector.sector, 1))
    return FAT_INIT_MEDIA_ACCESS_ERROR;
  if (fs->currentsector.sector[SIGNATURE_POSITION] != 0x55 ||
      fs->currentsector.sector[SIGNATURE_POSITION + 1] != 0xAA)
    return FAT_INIT_INVALID_SIGNATURE;
  if (GET_16BIT_WORD (fs->currentsector.sector, SIGNATURE_POSITION) != SIGNATURE_VALUE)
    return FAT_INIT_ENDIAN_ERROR;
  if (sizeof (struct fat_dir_entry) != FAT_DIR_ENTRY_SIZE)
    return FAT_INIT_STRUCT_PACKING;
  switch (fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION]) {
  case 0x0B:
  case 0x06:
  case 0x0C:
  case 0x0E:
  case 0x0F:
  case 0x05:
    valid_partition = 1;
    break;
  case 0x00:
    valid_partition = 0;
    break;
  default:
    if (fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION] <= 0x06)
      valid_partition = 1;
    break;
  }
  if (valid_partition)
    fs->lba_begin = GET_32BIT_WORD (fs->currentsector.sector, PARTITION1_LBA_BEGIN_LOCATION);
  else
    fs->lba_begin = 0;
  if (!fs->disk_io.read_media (ctx, fs->lba_begin, fs->currentsector.sector, 1))
    return FAT_INIT_MEDIA_ACCESS_ERROR;
  if (GET_16BIT_WORD (fs->currentsector.sector, 0x0B) != FAT_SECTOR_SIZE)
    return FAT_INIT_INVALID_SECTOR_SIZE;
  fs->sectors_per_cluster = fs->currentsector.sector[BPB_SECPERCLUS];
  reserved_sectors = GET_16BIT_WORD (fs->currentsector.sector, BPB_RSVDSECCNT);
  num_of_fats = fs->currentsector.sector[BPB_NUMFATS];
  fs->root_entry_count = GET_16BIT_WORD (fs->currentsector.sector, BPB_ROOTENTCNT);
  if (GET_16BIT_WORD (fs->currentsector.sector, BPB_FATSZ16) != 0)
    fs->fat_sectors = GET_16BIT_WORD (fs->currentsector.sector, BPB_FATSZ16);
  else
    fs->fat_sectors = GET_32BIT_WORD (fs->currentsector.sector, BPB_FAT32_FATSZ32);
  fs->rootdir_first_cluster = GET_32BIT_WORD (fs->currentsector.sector, BPB_FAT32_ROOTCLUS);
  fs->fs_info_sector = GET_16BIT_WORD (fs->currentsector.sector, BPB_FAT32_FSINFO);
  fs->rootdir_first_sector = reserved_sectors + (num_of_fats * fs->fat_sectors);
  fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
  fs->fat_begin_lba = fs->lba_begin + reserved_sectors;
  fs->cluster_begin_lba = fs->fat_begin_lba + (num_of_fats * fs->fat_sectors);
  if (GET_16BIT_WORD (fs->currentsector.sector, 0x1FE) != 0xAA55)
    return FAT_INIT_INVALID_SIGNATURE;
  root_dir_sectors = ((GET_16BIT_WORD (fs->currentsector.sector, BPB_ROOTENTCNT) * 32) +
                      (GET_16BIT_WORD (fs->currentsector.sector, BPB_BYTSPERSEC) - 1)) /
      GET_16BIT_WORD (fs->currentsector.sector, BPB_BYTSPERSEC);
  if (GET_16BIT_WORD (fs->currentsector.sector, BPB_FATSZ16) != 0)
    FATSz = GET_16BIT_WORD (fs->currentsector.sector, BPB_FATSZ16);
  else
    FATSz = GET_32BIT_WORD (fs->currentsector.sector, BPB_FAT32_FATSZ32);
  if (GET_16BIT_WORD (fs->currentsector.sector, BPB_TOTSEC16) != 0)
    total_sectors = GET_16BIT_WORD (fs->currentsector.sector, BPB_TOTSEC16);
  else
    total_sectors = GET_32BIT_WORD (fs->currentsector.sector, BPB_TOTSEC32);
  data_sectors = total_sectors -
      (GET_16BIT_WORD (fs->currentsector.sector, BPB_RSVDSECCNT) +
       (fs->currentsector.sector[BPB_NUMFATS] * FATSz) + root_dir_sectors);
  if (fs->sectors_per_cluster != 0) {
    count_of_clusters = data_sectors / fs->sectors_per_cluster;
    if (count_of_clusters < 4085)
      return FAT_INIT_WRONG_FILESYS_TYPE;
    else if (count_of_clusters < 65525) {
      fs->rootdir_first_cluster = 0;
      fs->fat_type = FAT_TYPE_16;
      return FAT_INIT_OK;
    } else {
      fs->fat_type = FAT_TYPE_32;
      return FAT_INIT_OK;
    }
  } else
    return FAT_INIT_WRONG_FILESYS_TYPE;
}
uint32 fatfs_lba_of_cluster (struct fatfs* fs, uint32 Cluster_Number) {
  if (fs->fat_type == FAT_TYPE_16)
    return (fs->cluster_begin_lba + (fs->root_entry_count * 32 / FAT_SECTOR_SIZE) +
            ((Cluster_Number - 2) * fs->sectors_per_cluster));
  else
    return ((fs->cluster_begin_lba + ((Cluster_Number - 2) * fs->sectors_per_cluster)));
}
int fatfs_sector_read (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 lba, uint8* target,
                       uint32 count) {
  return fs->disk_io.read_media (ctx, lba, target, count);
}
int fatfs_sector_write (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 lba, uint8* target,
                        uint32 count) {
  return fs->disk_io.write_media (ctx, lba, target, count);
}
int fatfs_sector_reader (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 start_cluster,
                         uint32 offset, uint8* target) {
  uint32 sector_to_read = 0;
  uint32 cluster_to_read = 0;
  uint32 cluster_chain = 0;
  uint32 i;
  uint32 lba;
  if (fs->fat_type == FAT_TYPE_16 && start_cluster == 0) {
    if (offset < fs->rootdir_sectors)
      lba = fs->lba_begin + fs->rootdir_first_sector + offset;
    else
      return 0;
  } else {
    cluster_chain = start_cluster;
    cluster_to_read = offset / fs->sectors_per_cluster;
    sector_to_read = offset - (cluster_to_read * fs->sectors_per_cluster);
    for (i = 0; i < cluster_to_read; i++)
      cluster_chain = fatfs_find_next_cluster (ctx, fs, cluster_chain);
    if (cluster_chain == FAT32_LAST_CLUSTER)
      return 0;
    lba = fatfs_lba_of_cluster (fs, cluster_chain) + sector_to_read;
  }
  if (target)
    return fs->disk_io.read_media (ctx, lba, target, 1);
  else if (lba != fs->currentsector.address) {
    fs->currentsector.address = lba;
    return fs->disk_io.read_media (ctx, fs->currentsector.address, fs->currentsector.sector, 1);
  } else
    return 1;
}
int fatfs_read_sector (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 cluster, uint32 sector,
                       uint8* target) {
  if (fs->fat_type == FAT_TYPE_16 && cluster == 0) {
    uint32 lba;
    if (sector < fs->rootdir_sectors)
      lba = fs->lba_begin + fs->rootdir_first_sector + sector;
    else
      return 0;
    if (target) {
      return fs->disk_io.read_media (ctx, lba, target, 1);
    } else {
      fs->currentsector.address = lba;
      return fs->disk_io.read_media (ctx, fs->currentsector.address, fs->currentsector.sector, 1);
    }
  } else {
    if (target) {
      uint32 lba = fatfs_lba_of_cluster (fs, cluster) + sector;
      return fs->disk_io.read_media (ctx, lba, target, 1);
    } else {
      fs->currentsector.address = fatfs_lba_of_cluster (fs, cluster) + sector;
      return fs->disk_io.read_media (ctx, fs->currentsector.address, fs->currentsector.sector, 1);
    }
  }
}
int fatfs_write_sector (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 cluster, uint32 sector,
                        uint8* target) {
  if (!fs->disk_io.write_media)
    return 0;
  if (fs->fat_type == FAT_TYPE_16 && cluster == 0) {
    uint32 lba;
    if (sector < fs->rootdir_sectors)
      lba = fs->lba_begin + fs->rootdir_first_sector + sector;
    else
      return 0;
    if (target) {
      return fs->disk_io.write_media (ctx, lba, target, 1);
    } else {
      fs->currentsector.address = lba;
      return fs->disk_io.write_media (ctx, fs->currentsector.address, fs->currentsector.sector, 1);
    }
  } else {
    if (target) {
      uint32 lba = fatfs_lba_of_cluster (fs, cluster) + sector;
      return fs->disk_io.write_media (ctx, lba, target, 1);
    } else {
      fs->currentsector.address = fatfs_lba_of_cluster (fs, cluster) + sector;
      return fs->disk_io.write_media (ctx, fs->currentsector.address, fs->currentsector.sector, 1);
    }
  }
}
void fatfs_show_details (struct fatfs* fs) {
  FAT_PRINTF (("FAT details:\r\n"));
  FAT_PRINTF ((" Type =%s", (fs->fat_type == FAT_TYPE_32) ? "FAT32" : "FAT16"));
  FAT_PRINTF ((" Root Dir First Cluster = %x\r\n", fs->rootdir_first_cluster));
  FAT_PRINTF ((" FAT Begin LBA = 0x%x\r\n", fs->fat_begin_lba));
  FAT_PRINTF ((" Cluster Begin LBA = 0x%x\r\n", fs->cluster_begin_lba));
  FAT_PRINTF ((" Sectors Per Cluster = %d\r\n", fs->sectors_per_cluster));
}
uint32 fatfs_get_root_cluster (struct fatfs* fs) { return fs->rootdir_first_cluster; }
uint32 fatfs_get_file_entry (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 Cluster,
                             char* name_to_find, struct fat_dir_entry* sfEntry) {
  uint8 item = 0;
  uint16 recordoffset = 0;
  uint8 i = 0;
  int x = 0;
  char* long_filename = NULL;
  char short_filename[13];
  struct lfn_cache lfn;
  int dotRequired = 0;
  struct fat_dir_entry* directoryEntry;
  fatfs_lfn_cache_init (&lfn, 1);
  while (1) {
    if (fatfs_sector_reader (ctx, fs, Cluster, x++, 0)) {
      for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++) {
        recordoffset = FAT_DIR_ENTRY_SIZE * item;
        directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector + recordoffset);
        if (fatfs_entry_lfn_text (directoryEntry))
          fatfs_lfn_cache_entry (&lfn, fs->currentsector.sector + recordoffset);
        else if (fatfs_entry_lfn_invalid (directoryEntry))
          fatfs_lfn_cache_init (&lfn, 0);
        else if (fatfs_entry_lfn_exists (&lfn, directoryEntry)) {
          long_filename = fatfs_lfn_cache_get (&lfn);
          if (fatfs_compare_names (long_filename, name_to_find)) {
            memcpy (sfEntry, directoryEntry, sizeof (struct fat_dir_entry));
            return 1;
          }
          fatfs_lfn_cache_init (&lfn, 0);
        } else if (fatfs_entry_sfn_only (directoryEntry)) {
          memset (short_filename, 0, sizeof (short_filename));
          for (i = 0; i < 8; i++)
            short_filename[i] = directoryEntry->Name[i];
          dotRequired = 0;
          for (i = 8; i < 11; i++) {
            short_filename[i + 1] = directoryEntry->Name[i];
            if (directoryEntry->Name[i] != ' ')
              dotRequired = 1;
          }
          if (dotRequired) {
            if (short_filename[0] != '.')
              short_filename[8] = '.';
            else
              short_filename[8] = ' ';
          } else
            short_filename[8] = ' ';
          if (fatfs_compare_names (short_filename, name_to_find)) {
            memcpy (sfEntry, directoryEntry, sizeof (struct fat_dir_entry));
            return 1;
          }
          fatfs_lfn_cache_init (&lfn, 0);
        }
      }
    } else
      break;
  }
  return 0;
}
int fatfs_sfn_exists (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 Cluster, char* shortname) {
  uint8 item = 0;
  uint16 recordoffset = 0;
  int x = 0;
  struct fat_dir_entry* directoryEntry;
  while (1) {
    if (fatfs_sector_reader (ctx, fs, Cluster, x++, 0)) {
      for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++) {
        recordoffset = FAT_DIR_ENTRY_SIZE * item;
        directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector + recordoffset);
        if (fatfs_entry_lfn_text (directoryEntry))
          ;
        else if (fatfs_entry_lfn_invalid (directoryEntry))
          ;
        else if (fatfs_entry_sfn_only (directoryEntry)) {
          if (strncmp ((const char*)directoryEntry->Name, shortname, 11) == 0)
            return 1;
        }
      }
    } else
      break;
  }
  return 0;
}
int fatfs_update_file_length (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 Cluster,
                              char* shortname, uint32 fileLength) {
  uint8 item = 0;
  uint16 recordoffset = 0;
  int x = 0;
  struct fat_dir_entry* directoryEntry;
  if (!fs->disk_io.write_media)
    return 0;
  while (1) {
    if (fatfs_sector_reader (ctx, fs, Cluster, x++, 0)) {
      for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++) {
        recordoffset = FAT_DIR_ENTRY_SIZE * item;
        directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector + recordoffset);
        if (fatfs_entry_lfn_text (directoryEntry))
          ;
        else if (fatfs_entry_lfn_invalid (directoryEntry))
          ;
        else if (fatfs_entry_sfn_only (directoryEntry)) {
          if (strncmp ((const char*)directoryEntry->Name, shortname, 11) == 0) {
            directoryEntry->FileSize = FAT_HTONL (fileLength);
            memcpy ((uint8*)(fs->currentsector.sector + recordoffset), (uint8*)directoryEntry,
                    sizeof (struct fat_dir_entry));
            return fs->disk_io.write_media (ctx, fs->currentsector.address,
                                            fs->currentsector.sector, 1);
          }
        }
      }
    } else
      break;
  }
  return 0;
}
int fatfs_mark_file_deleted (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 Cluster,
                             char* shortname) {
  uint8 item = 0;
  uint16 recordoffset = 0;
  int x = 0;
  struct fat_dir_entry* directoryEntry;
  if (!fs->disk_io.write_media)
    return 0;
  while (1) {
    if (fatfs_sector_reader (ctx, fs, Cluster, x++, 0)) {
      for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++) {
        recordoffset = FAT_DIR_ENTRY_SIZE * item;
        directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector + recordoffset);
        if (fatfs_entry_lfn_text (directoryEntry))
          ;
        else if (fatfs_entry_lfn_invalid (directoryEntry))
          ;
        else if (fatfs_entry_sfn_only (directoryEntry)) {
          if (strncmp ((const char*)directoryEntry->Name, shortname, 11) == 0) {
            directoryEntry->Name[0] = FILE_HEADER_DELETED;
            memcpy ((uint8*)(fs->currentsector.sector + recordoffset), (uint8*)directoryEntry,
                    sizeof (struct fat_dir_entry));
            return fs->disk_io.write_media (ctx, fs->currentsector.address,
                                            fs->currentsector.sector, 1);
          }
        }
      }
    } else
      break;
  }
  return 0;
}
void fatfs_list_directory_start (struct fatfs* fs, struct fs_dir_list_status* dirls,
                                 uint32 StartCluster) {
  dirls->cluster = StartCluster;
  dirls->sector = 0;
  dirls->offset = 0;
}
int fatfs_list_directory_next (struct fatfs_ctx* ctx, struct fatfs* fs,
                               struct fs_dir_list_status* dirls, struct fs_dir_ent* entry) {
  uint8 i, item;
  uint16 recordoffset;
  struct fat_dir_entry* directoryEntry;
  char* long_filename = NULL;
  char short_filename[13];
  struct lfn_cache lfn;
  int dotRequired = 0;
  int result = 0;
  fatfs_lfn_cache_init (&lfn, 0);
  while (1) {
    if (fatfs_sector_reader (ctx, fs, dirls->cluster, dirls->sector, 0)) {
      for (item = dirls->offset; item < FAT_DIR_ENTRIES_PER_SECTOR; item++) {
        recordoffset = FAT_DIR_ENTRY_SIZE * item;
        directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector + recordoffset);
        if (fatfs_entry_lfn_text (directoryEntry))
          fatfs_lfn_cache_entry (&lfn, fs->currentsector.sector + recordoffset);
        else if (fatfs_entry_lfn_invalid (directoryEntry))
          fatfs_lfn_cache_init (&lfn, 0);
        else if (fatfs_entry_lfn_exists (&lfn, directoryEntry)) {
          long_filename = fatfs_lfn_cache_get (&lfn);
          strncpy (entry->filename, long_filename, FATFS_MAX_LONG_FILENAME - 1);
          if (fatfs_entry_is_dir (directoryEntry))
            entry->is_dir = 1;
          else
            entry->is_dir = 0;
          entry->size = FAT_HTONL (directoryEntry->FileSize);
          entry->cluster =
              (FAT_HTONS (directoryEntry->FstClusHI) << 16) | FAT_HTONS (directoryEntry->FstClusLO);
          dirls->offset = item + 1;
          result = 1;
          return 1;
        } else if (fatfs_entry_sfn_only (directoryEntry)) {
          fatfs_lfn_cache_init (&lfn, 0);
          memset (short_filename, 0, sizeof (short_filename));
          for (i = 0; i < 8; i++)
            short_filename[i] = directoryEntry->Name[i];
          dotRequired = 0;
          for (i = 8; i < 11; i++) {
            short_filename[i + 1] = directoryEntry->Name[i];
            if (directoryEntry->Name[i] != ' ')
              dotRequired = 1;
          }
          if (dotRequired) {
            if (short_filename[0] != '.')
              short_filename[8] = '.';
            else
              short_filename[8] = ' ';
          } else
            short_filename[8] = ' ';
          fatfs_get_sfn_display_name (entry->filename, short_filename);
          if (fatfs_entry_is_dir (directoryEntry))
            entry->is_dir = 1;
          else
            entry->is_dir = 0;
          entry->size = FAT_HTONL (directoryEntry->FileSize);
          entry->cluster =
              (FAT_HTONS (directoryEntry->FstClusHI) << 16) | FAT_HTONS (directoryEntry->FstClusLO);
          dirls->offset = item + 1;
          result = 1;
          return 1;
        }
      }
      dirls->sector++;
      dirls->offset = 0;
    } else
      break;
  }
  return result;
}

#define __FAT_FORMAT_H__
int fatfs_format (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 volume_sectors, const char* name);
int fatfs_format_fat16 (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 volume_sectors,
                        const char* name);
int fatfs_format_fat32 (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 volume_sectors,
                        const char* name);
struct sec_per_clus_table {
  uint32 sectors;
  uint8 sectors_per_cluster;
};
struct sec_per_clus_table _cluster_size_table16[] = {{32680, 2},     {262144, 4},   {524288, 8},
                                                     {1048576, 16},  {2097152, 32}, {4194304, 64},
                                                     {8388608, 128}, {0, 0}};
struct sec_per_clus_table _cluster_size_table32[] = {
    {532480, 1}, {16777216, 8}, {33554432, 16}, {67108864, 32}, {0xFFFFFFFF, 64}, {0, 0}};
static uint8 fatfs_calc_cluster_size (uint32 sectors, int is_fat32) {
  int i;
  if (!is_fat32) {
    for (i = 0; _cluster_size_table16[i].sectors_per_cluster != 0; i++)
      if (sectors <= _cluster_size_table16[i].sectors)
        return _cluster_size_table16[i].sectors_per_cluster;
  } else {
    for (i = 0; _cluster_size_table32[i].sectors_per_cluster != 0; i++)
      if (sectors <= _cluster_size_table32[i].sectors)
        return _cluster_size_table32[i].sectors_per_cluster;
  }
  return 0;
}
static int fatfs_erase_sectors (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 lba, int count) {
  int i;
  memset (fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
  for (i = 0; i < count; i++)
    if (!fs->disk_io.write_media (ctx, lba + i, fs->currentsector.sector, 1))
      return 0;
  return 1;
}
static int fatfs_create_boot_sector (struct fatfs_ctx* ctx, struct fatfs* fs,
                                     uint32 boot_sector_lba, uint32 vol_sectors, const char* name,
                                     int is_fat32) {
  uint32 total_clusters;
  int i;
  memset (fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
  fs->currentsector.sector[0] = 0xEB;
  fs->currentsector.sector[1] = 0x3C;
  fs->currentsector.sector[2] = 0x90;
  fs->currentsector.sector[3] = 0x4D;
  fs->currentsector.sector[4] = 0x53;
  fs->currentsector.sector[5] = 0x44;
  fs->currentsector.sector[6] = 0x4F;
  fs->currentsector.sector[7] = 0x53;
  fs->currentsector.sector[8] = 0x35;
  fs->currentsector.sector[9] = 0x2E;
  fs->currentsector.sector[10] = 0x30;
  fs->currentsector.sector[11] = (FAT_SECTOR_SIZE >> 0) & 0xFF;
  fs->currentsector.sector[12] = (FAT_SECTOR_SIZE >> 8) & 0xFF;
  fs->sectors_per_cluster = fatfs_calc_cluster_size (vol_sectors, is_fat32);
  if (!fs->sectors_per_cluster)
    return 0;
  fs->currentsector.sector[13] = fs->sectors_per_cluster;
  if (!is_fat32)
    fs->reserved_sectors = 8;
  else
    fs->reserved_sectors = 32;
  fs->currentsector.sector[14] = (fs->reserved_sectors >> 0) & 0xFF;
  fs->currentsector.sector[15] = (fs->reserved_sectors >> 8) & 0xFF;
  fs->num_of_fats = 2;
  fs->currentsector.sector[16] = fs->num_of_fats;
  if (!is_fat32) {
    fs->root_entry_count = 512;
    fs->currentsector.sector[17] = (fs->root_entry_count >> 0) & 0xFF;
    fs->currentsector.sector[18] = (fs->root_entry_count >> 8) & 0xFF;
  } else {
    fs->root_entry_count = 0;
    fs->currentsector.sector[17] = 0;
    fs->currentsector.sector[18] = 0;
  }
  fs->currentsector.sector[19] = 0x00;
  fs->currentsector.sector[20] = 0x00;
  fs->currentsector.sector[21] = 0xF8;
  if (!is_fat32) {
    total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
    fs->fat_sectors = (total_clusters / (FAT_SECTOR_SIZE / 2)) + 1;
    fs->currentsector.sector[22] = (uint8)((fs->fat_sectors >> 0) & 0xFF);
    fs->currentsector.sector[23] = (uint8)((fs->fat_sectors >> 8) & 0xFF);
    fs->currentsector.sector[24] = 0x00;
    fs->currentsector.sector[25] = 0x00;
    fs->currentsector.sector[26] = 0x00;
    fs->currentsector.sector[27] = 0x00;
    fs->currentsector.sector[28] = 0x20;
    fs->currentsector.sector[29] = 0x00;
    fs->currentsector.sector[30] = 0x00;
    fs->currentsector.sector[31] = 0x00;
    fs->currentsector.sector[32] = (uint8)((vol_sectors >> 0) & 0xFF);
    fs->currentsector.sector[33] = (uint8)((vol_sectors >> 8) & 0xFF);
    fs->currentsector.sector[34] = (uint8)((vol_sectors >> 16) & 0xFF);
    fs->currentsector.sector[35] = (uint8)((vol_sectors >> 24) & 0xFF);
    fs->currentsector.sector[36] = 0x00;
    fs->currentsector.sector[37] = 0x00;
    fs->currentsector.sector[38] = 0x29;
    fs->currentsector.sector[39] = 0x12;
    fs->currentsector.sector[40] = 0x34;
    fs->currentsector.sector[41] = 0x56;
    fs->currentsector.sector[42] = 0x78;
    for (i = 0; i < 11; i++) {
      if (i < (int)strlen (name))
        fs->currentsector.sector[i + 43] = name[i];
      else
        fs->currentsector.sector[i + 43] = ' ';
    }
    fs->currentsector.sector[54] = 'F';
    fs->currentsector.sector[55] = 'A';
    fs->currentsector.sector[56] = 'T';
    fs->currentsector.sector[57] = '1';
    fs->currentsector.sector[58] = '6';
    fs->currentsector.sector[59] = ' ';
    fs->currentsector.sector[60] = ' ';
    fs->currentsector.sector[61] = ' ';
    fs->currentsector.sector[510] = 0x55;
    fs->currentsector.sector[511] = 0xAA;
  } else {
    fs->currentsector.sector[22] = 0;
    fs->currentsector.sector[23] = 0;
    fs->currentsector.sector[24] = 0x3F;
    fs->currentsector.sector[25] = 0x00;
    fs->currentsector.sector[26] = 0xFF;
    fs->currentsector.sector[27] = 0x00;
    fs->currentsector.sector[28] = 0x00;
    fs->currentsector.sector[29] = 0x00;
    fs->currentsector.sector[30] = 0x00;
    fs->currentsector.sector[31] = 0x00;
    fs->currentsector.sector[32] = (uint8)((vol_sectors >> 0) & 0xFF);
    fs->currentsector.sector[33] = (uint8)((vol_sectors >> 8) & 0xFF);
    fs->currentsector.sector[34] = (uint8)((vol_sectors >> 16) & 0xFF);
    fs->currentsector.sector[35] = (uint8)((vol_sectors >> 24) & 0xFF);
    total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
    fs->fat_sectors = (total_clusters / (FAT_SECTOR_SIZE / 4)) + 1;
    fs->currentsector.sector[36] = (uint8)((fs->fat_sectors >> 0) & 0xFF);
    fs->currentsector.sector[37] = (uint8)((fs->fat_sectors >> 8) & 0xFF);
    fs->currentsector.sector[38] = (uint8)((fs->fat_sectors >> 16) & 0xFF);
    fs->currentsector.sector[39] = (uint8)((fs->fat_sectors >> 24) & 0xFF);
    fs->currentsector.sector[40] = 0;
    fs->currentsector.sector[41] = 0;
    fs->currentsector.sector[42] = 0;
    fs->currentsector.sector[43] = 0;
    fs->currentsector.sector[44] = (uint8)((fs->rootdir_first_cluster >> 0) & 0xFF);
    fs->currentsector.sector[45] = (uint8)((fs->rootdir_first_cluster >> 8) & 0xFF);
    fs->currentsector.sector[46] = (uint8)((fs->rootdir_first_cluster >> 16) & 0xFF);
    fs->currentsector.sector[47] = (uint8)((fs->rootdir_first_cluster >> 24) & 0xFF);
    fs->currentsector.sector[48] = (uint8)((fs->fs_info_sector >> 0) & 0xFF);
    fs->currentsector.sector[49] = (uint8)((fs->fs_info_sector >> 8) & 0xFF);
    fs->currentsector.sector[50] = 6;
    fs->currentsector.sector[51] = 0;
    fs->currentsector.sector[64] = 0x00;
    fs->currentsector.sector[66] = 0x29;
    fs->currentsector.sector[67] = 0x12;
    fs->currentsector.sector[68] = 0x34;
    fs->currentsector.sector[69] = 0x56;
    fs->currentsector.sector[70] = 0x78;
    for (i = 0; i < 11; i++) {
      if (i < (int)strlen (name))
        fs->currentsector.sector[i + 71] = name[i];
      else
        fs->currentsector.sector[i + 71] = ' ';
    }
    fs->currentsector.sector[82] = 'F';
    fs->currentsector.sector[83] = 'A';
    fs->currentsector.sector[84] = 'T';
    fs->currentsector.sector[85] = '3';
    fs->currentsector.sector[86] = '2';
    fs->currentsector.sector[87] = ' ';
    fs->currentsector.sector[88] = ' ';
    fs->currentsector.sector[89] = ' ';
    fs->currentsector.sector[510] = 0x55;
    fs->currentsector.sector[511] = 0xAA;
  }
  if (fs->disk_io.write_media (ctx, boot_sector_lba, fs->currentsector.sector, 1))
    return 1;
  else
    return 0;
}
static int fatfs_create_fsinfo_sector (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 sector_lba) {
  memset (fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
  fs->currentsector.sector[0] = 0x52;
  fs->currentsector.sector[1] = 0x52;
  fs->currentsector.sector[2] = 0x61;
  fs->currentsector.sector[3] = 0x41;
  fs->currentsector.sector[484] = 0x72;
  fs->currentsector.sector[485] = 0x72;
  fs->currentsector.sector[486] = 0x41;
  fs->currentsector.sector[487] = 0x61;
  fs->currentsector.sector[488] = 0xFF;
  fs->currentsector.sector[489] = 0xFF;
  fs->currentsector.sector[490] = 0xFF;
  fs->currentsector.sector[491] = 0xFF;
  fs->currentsector.sector[492] = 0xFF;
  fs->currentsector.sector[493] = 0xFF;
  fs->currentsector.sector[494] = 0xFF;
  fs->currentsector.sector[495] = 0xFF;
  fs->currentsector.sector[510] = 0x55;
  fs->currentsector.sector[511] = 0xAA;
  if (fs->disk_io.write_media (ctx, sector_lba, fs->currentsector.sector, 1))
    return 1;
  else
    return 0;
}
static int fatfs_erase_fat (struct fatfs_ctx* ctx, struct fatfs* fs, int is_fat32) {
  uint32 i;
  memset (fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
  if (!is_fat32) {
    SET_16BIT_WORD (fs->currentsector.sector, 0, 0xFFF8);
    SET_16BIT_WORD (fs->currentsector.sector, 2, 0xFFFF);
  } else {
    SET_32BIT_WORD (fs->currentsector.sector, 0, 0x0FFFFFF8);
    SET_32BIT_WORD (fs->currentsector.sector, 4, 0xFFFFFFFF);
    SET_32BIT_WORD (fs->currentsector.sector, 8, 0x0FFFFFFF);
  }
  if (!fs->disk_io.write_media (ctx, fs->fat_begin_lba + 0, fs->currentsector.sector, 1))
    return 0;
  memset (fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
  for (i = 1; i < fs->fat_sectors * fs->num_of_fats; i++)
    if (!fs->disk_io.write_media (ctx, fs->fat_begin_lba + i, fs->currentsector.sector, 1))
      return 0;
  return 1;
}
int fatfs_format_fat16 (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 volume_sectors,
                        const char* name) {
  fs->currentsector.address = FAT32_INVALID_CLUSTER;
  fs->currentsector.dirty = 0;
  fs->next_free_cluster = 0;
  fatfs_fat_init (fs);
  if (!fs->disk_io.read_media || !fs->disk_io.write_media)
    return FAT_INIT_MEDIA_ACCESS_ERROR;
  fs->fat_type = FAT_TYPE_16;
  fs->fs_info_sector = 0;
  fs->rootdir_first_cluster = 0;
  fs->lba_begin = 0;
  if (!fatfs_create_boot_sector (ctx, fs, fs->lba_begin, volume_sectors, name, 0))
    return 0;
  fs->rootdir_first_sector = fs->reserved_sectors + (fs->num_of_fats * fs->fat_sectors);
  fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
  fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
  fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
  if (!fatfs_erase_fat (ctx, fs, 0))
    return 0;
  if (!fatfs_erase_sectors (ctx, fs, fs->lba_begin + fs->rootdir_first_sector, fs->rootdir_sectors))
    return 0;
  return 1;
}
int fatfs_format_fat32 (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 volume_sectors,
                        const char* name) {
  fs->currentsector.address = FAT32_INVALID_CLUSTER;
  fs->currentsector.dirty = 0;
  fs->next_free_cluster = 0;
  fatfs_fat_init (fs);
  if (!fs->disk_io.read_media || !fs->disk_io.write_media)
    return FAT_INIT_MEDIA_ACCESS_ERROR;
  fs->fat_type = FAT_TYPE_32;
  fs->fs_info_sector = 1;
  fs->rootdir_first_cluster = 2;
  fs->lba_begin = 0;
  if (!fatfs_create_boot_sector (ctx, fs, fs->lba_begin, volume_sectors, name, 1))
    return 0;
  fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
  fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
  if (!fatfs_create_fsinfo_sector (ctx, fs, fs->fs_info_sector))
    return 0;
  if (!fatfs_erase_fat (ctx, fs, 1))
    return 0;
  if (!fatfs_erase_sectors (ctx, fs, fatfs_lba_of_cluster (fs, fs->rootdir_first_cluster),
                            fs->sectors_per_cluster))
    return 0;
  return 1;
}
int fatfs_format (struct fatfs_ctx* ctx, struct fatfs* fs, uint32 volume_sectors,
                  const char* name) {
  if (volume_sectors <= 4194304)
    return fatfs_format_fat16 (ctx, fs, volume_sectors, name);
  else
    return fatfs_format_fat32 (ctx, fs, volume_sectors, name);
}
int fatfs_total_path_levels (char* path) {
  int levels = 0;
  char expectedchar;
  if (!path)
    return -1;
  if (*path == '/') {
    expectedchar = '/';
    path++;
  } else if (path[1] == ':' || path[2] == '\\') {
    expectedchar = '\\';
    path += 3;
  } else
    return -1;
  while (*path) {
    for (; *path;) {
      if (*path == expectedchar) {
        path++;
        break;
      }
      path++;
    }
    levels++;
  }
  return levels - 1;
}
int fatfs_get_substring (char* path, int levelreq, char* output, int max_len) {
  int i;
  int pathlen = 0;
  int levels = 0;
  int copypnt = 0;
  char expectedchar;
  if (!path || max_len <= 0)
    return -1;
  if (*path == '/') {
    expectedchar = '/';
    path++;
  } else if (path[1] == ':' || path[2] == '\\') {
    expectedchar = '\\';
    path += 3;
  } else
    return -1;
  pathlen = (int)strlen (path);
  for (i = 0; i < pathlen; i++) {
    if (*path == expectedchar)
      levels++;
    if ((levels == levelreq) && (*path != expectedchar) && (copypnt < (max_len - 1)))
      output[copypnt++] = *path;
    path++;
  }
  output[copypnt] = '\0';
  if (output[0] != '\0')
    return 0;
  else
    return -1;
}
int fatfs_split_path (char* full_path, char* path, int max_path, char* filename, int max_filename) {
  int strindex;
  int levels = fatfs_total_path_levels (full_path);
  if (levels == -1)
    return -1;
  if (fatfs_get_substring (full_path, levels, filename, max_filename) != 0)
    return -1;
  if (levels == 0)
    path[0] = '\0';
  else {
    strindex = (int)strlen (full_path) - (int)strlen (filename);
    if (strindex > max_path)
      strindex = max_path;
    memcpy (path, full_path, strindex);
    path[strindex - 1] = '\0';
  }
  return 0;
}
static int FileString_StrCmpNoCase (char* s1, char* s2, int n) {
  int diff;
  char a, b;
  while (n--) {
    a = *s1;
    b = *s2;
    if ((a >= 'A') && (a <= 'Z'))
      a += 32;
    if ((b >= 'A') && (b <= 'Z'))
      b += 32;
    diff = a - b;
    if (diff)
      return diff;
    if ((*s1 == 0) || (*s2 == 0))
      break;
    s1++;
    s2++;
  }
  return 0;
}
static int FileString_GetExtension (char* str) {
  int dotPos = -1;
  char* strSrc = str;
  while (*strSrc) {
    if (*strSrc == '.')
      dotPos = (int)(strSrc - str);
    strSrc++;
  }
  return dotPos;
}
static int FileString_TrimLength (char* str, int strLen) {
  int length = strLen;
  char* strSrc = str + strLen - 1;
  while (strLen != 0) {
    if (*strSrc == ' ')
      length = (int)(strSrc - str);
    else
      break;
    strSrc--;
    strLen--;
  }
  return length;
}
int fatfs_compare_names (char* strA, char* strB) {
  char* ext1 = NULL;
  char* ext2 = NULL;
  int ext1Pos, ext2Pos;
  int file1Len, file2Len;
  ext1Pos = FileString_GetExtension (strA);
  ext2Pos = FileString_GetExtension (strB);
  if ((ext1Pos == -1) && (ext2Pos != -1))
    return 0;
  if ((ext2Pos == -1) && (ext1Pos != -1))
    return 0;
  if (ext1Pos != -1) {
    ext1 = strA + ext1Pos + 1;
    ext2 = strB + ext2Pos + 1;
    if (strlen (ext1) != strlen (ext2))
      return 0;
    if (FileString_StrCmpNoCase (ext1, ext2, (int)strlen (ext1)) != 0)
      return 0;
    file1Len = ext1Pos;
    file2Len = ext2Pos;
  } else {
    file1Len = (int)strlen (strA);
    file2Len = (int)strlen (strB);
  }
  file1Len = FileString_TrimLength (strA, file1Len);
  file2Len = FileString_TrimLength (strB, file2Len);
  if (file1Len != file2Len)
    return 0;
  if (FileString_StrCmpNoCase (strA, strB, file1Len) != 0)
    return 0;
  else
    return 1;
}
int fatfs_string_ends_with_slash (char* path) {
  if (path) {
    while (*path) {
      if (!(*(path + 1))) {
        if (*path == '\\' || *path == '/')
          return 1;
      }
      path++;
    }
  }
  return 0;
}
int fatfs_get_sfn_display_name (char* out, char* in) {
  int len = 0;
  while (*in && len <= 11) {
    char a = *in++;
    if (a == ' ')
      continue;
    else if ((a >= 'A') && (a <= 'Z'))
      a += 32;
    *out++ = a;
    len++;
  }
  *out = '\0';
  return 1;
}
int fatfs_get_extension (char* filename, char* out, int maxlen) {
  int len = 0;
  int ext_pos = FileString_GetExtension (filename);
  if (ext_pos > 0 && out && maxlen) {
    filename += ext_pos + 1;
    while (*filename && len < (maxlen - 1)) {
      char a = *filename++;
      if ((a >= 'A') && (a <= 'Z'))
        a += 32;
      *out++ = a;
      len++;
    }
    *out = '\0';
    return 1;
  }
  return 0;
}
int fatfs_create_path_string (char* path, char* filename, char* out, int maxlen) {
  int len = 0;
  char last = 0;
  char seperator = '/';
  if (path && filename && out && maxlen > 0) {
    while (*path && len < (maxlen - 2)) {
      last = *path++;
      if (last == '\\')
        seperator = '\\';
      *out++ = last;
      len++;
    }
    if (last != '\\' && last != '/')
      *out++ = seperator;
    while (*filename && len < (maxlen - 1)) {
      *out++ = *filename++;
      len++;
    }
    *out = '\0';
    return 1;
  }
  return 0;
}
#define CHECK_FL_INIT(ctx)                                                                         \
  {                                                                                                \
    if (ctx->_filelib_init == 0)                                                                   \
      fl_init (ctx);                                                                               \
  }
#define FL_LOCK(a)                                                                                 \
  do {                                                                                             \
    if ((a)->fl_lock)                                                                              \
      (a)->fl_lock ();                                                                             \
  } while (0)
#define FL_UNLOCK(a)                                                                               \
  do {                                                                                             \
    if ((a)->fl_unlock)                                                                            \
      (a)->fl_unlock ();                                                                           \
  } while (0)
void fl_init (struct fatfs_ctx* ctx);
static FL_FILE* _allocate_file (struct fatfs_ctx* ctx) {
  struct fat_node* node = fat_list_pop_head (&ctx->_free_file_list);
  if (node)
    fat_list_insert_last (&ctx->_open_file_list, node);
  return fat_list_entry (node, FL_FILE, list_node);
}
static int _check_file_open (struct fatfs_ctx* ctx, FL_FILE* file) {
  struct fat_node* node;
  fat_list_for_each (&ctx->_open_file_list, node) {
    FL_FILE* openFile = fat_list_entry (node, FL_FILE, list_node);
    if (openFile != file) {
      if ((fatfs_compare_names (openFile->path, file->path)) &&
          (fatfs_compare_names (openFile->filename, file->filename)))
        return 1;
    }
  }
  return 0;
}
static void _free_file (struct fatfs_ctx* ctx, FL_FILE* file) {
  fat_list_remove (&ctx->_open_file_list, &file->list_node);
  fat_list_insert_last (&ctx->_free_file_list, &file->list_node);
}
static int _open_directory (struct fatfs_ctx* ctx, char* path, uint32* pathCluster) {
  int levels;
  int sublevel;
  char currentfolder[FATFS_MAX_LONG_FILENAME];
  struct fat_dir_entry sfEntry;
  uint32 startcluster;
  startcluster = fatfs_get_root_cluster (&ctx->_fs);
  levels = fatfs_total_path_levels (path);
  for (sublevel = 0; sublevel < (levels + 1); sublevel++) {
    if (fatfs_get_substring (path, sublevel, currentfolder, sizeof (currentfolder)) == -1)
      return 0;
    if (fatfs_get_file_entry (ctx, &ctx->_fs, startcluster, currentfolder, &sfEntry)) {
      if (fatfs_entry_is_dir (&sfEntry))
        startcluster =
            ((FAT_HTONS ((uint32)sfEntry.FstClusHI)) << 16) + FAT_HTONS (sfEntry.FstClusLO);
      else
        return 0;
    } else
      return 0;
  }
  *pathCluster = startcluster;
  return 1;
}
static int _create_directory (struct fatfs_ctx* ctx, char* path) {
  FL_FILE* file;
  struct fat_dir_entry sfEntry;
  char shortFilename[FAT_SFN_SIZE_FULL];
  int tailNum = 0;
  int i;
  file = _allocate_file (ctx);
  if (!file)
    return 0;
  memset (file->path, '\0', sizeof (file->path));
  memset (file->filename, '\0', sizeof (file->filename));
  if (fatfs_split_path ((char*)path, file->path, sizeof (file->path), file->filename,
                        sizeof (file->filename)) == -1) {
    _free_file (ctx, file);
    return 0;
  }
  if (_check_file_open (ctx, file)) {
    _free_file (ctx, file);
    return 0;
  }
  if (file->path[0] == 0)
    file->parentcluster = fatfs_get_root_cluster (&ctx->_fs);
  else {
    if (!_open_directory (ctx, file->path, &file->parentcluster)) {
      _free_file (ctx, file);
      return 0;
    }
  }
  if (fatfs_get_file_entry (ctx, &ctx->_fs, file->parentcluster, file->filename, &sfEntry) == 1) {
    _free_file (ctx, file);
    return 0;
  }
  file->startcluster = 0;
  if (!fatfs_allocate_free_space (ctx, &ctx->_fs, 1, &file->startcluster, 1)) {
    _free_file (ctx, file);
    return 0;
  }
  memset (file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
  for (i = 0; i < ctx->_fs.sectors_per_cluster; i++) {
    if (!fatfs_write_sector (ctx, &ctx->_fs, file->startcluster, i, file->file_data_sector)) {
      _free_file (ctx, file);
      return 0;
    }
  }
  tailNum = 0;
  do {
    fatfs_lfn_create_sfn (shortFilename, file->filename);
    if (tailNum != 0)
      fatfs_lfn_generate_tail ((char*)file->shortfilename, shortFilename, tailNum);
    else
      memcpy (file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
    if (fatfs_sfn_exists (ctx, &ctx->_fs, file->parentcluster, (char*)file->shortfilename) == 0)
      break;
    tailNum++;
  } while (tailNum < 9999);
  if (tailNum == 9999) {
    fatfs_free_cluster_chain (ctx, &ctx->_fs, file->startcluster);
    _free_file (ctx, file);
    return 0;
  }
  if (!fatfs_add_file_entry (ctx, &ctx->_fs, file->parentcluster, (char*)file->filename,
                             (char*)file->shortfilename, file->startcluster, 0, 1)) {
    fatfs_free_cluster_chain (ctx, &ctx->_fs, file->startcluster);
    _free_file (ctx, file);
    return 0;
  }
  file->filelength = 0;
  file->bytenum = 0;
  file->file_data_address = 0xFFFFFFFF;
  file->file_data_dirty = 0;
  file->filelength_changed = 0;
  file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
  file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
  fatfs_fat_purge (ctx, &ctx->_fs);
  _free_file (ctx, file);
  return 1;
}
static FL_FILE* _open_file (struct fatfs_ctx* ctx, const char* path) {
  FL_FILE* file;
  struct fat_dir_entry sfEntry;
  file = _allocate_file (ctx);
  if (!file)
    return NULL;
  memset (file->path, '\0', sizeof (file->path));
  memset (file->filename, '\0', sizeof (file->filename));
  if (fatfs_split_path ((char*)path, file->path, sizeof (file->path), file->filename,
                        sizeof (file->filename)) == -1) {
    _free_file (ctx, file);
    return NULL;
  }
  if (_check_file_open (ctx, file)) {
    _free_file (ctx, file);
    return NULL;
  }
  if (file->path[0] == 0)
    file->parentcluster = fatfs_get_root_cluster (&ctx->_fs);
  else {
    if (!_open_directory (ctx, file->path, &file->parentcluster)) {
      _free_file (ctx, file);
      return NULL;
    }
  }
  if (fatfs_get_file_entry (ctx, &ctx->_fs, file->parentcluster, file->filename, &sfEntry))
    if (fatfs_entry_is_file (&sfEntry)) {
      memcpy (file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
      file->filelength = FAT_HTONL (sfEntry.FileSize);
      file->bytenum = 0;
      file->startcluster =
          ((FAT_HTONS ((uint32)sfEntry.FstClusHI)) << 16) + FAT_HTONS (sfEntry.FstClusLO);
      file->file_data_address = 0xFFFFFFFF;
      file->file_data_dirty = 0;
      file->filelength_changed = 0;
      file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
      file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
      fatfs_cache_init (&ctx->_fs, file);
      fatfs_fat_purge (ctx, &ctx->_fs);
      return file;
    }
  _free_file (ctx, file);
  return NULL;
}
static FL_FILE* _create_file (struct fatfs_ctx* ctx, const char* filename) {
  FL_FILE* file;
  struct fat_dir_entry sfEntry;
  char shortFilename[FAT_SFN_SIZE_FULL];
  int tailNum = 0;
  if (!ctx->_fs.disk_io.write_media)
    return NULL;
  file = _allocate_file (ctx);
  if (!file)
    return NULL;
  memset (file->path, '\0', sizeof (file->path));
  memset (file->filename, '\0', sizeof (file->filename));
  if (fatfs_split_path ((char*)filename, file->path, sizeof (file->path), file->filename,
                        sizeof (file->filename)) == -1) {
    _free_file (ctx, file);
    return NULL;
  }
  if (_check_file_open (ctx, file)) {
    _free_file (ctx, file);
    return NULL;
  }
  if (file->path[0] == 0)
    file->parentcluster = fatfs_get_root_cluster (&ctx->_fs);
  else {
    if (!_open_directory (ctx, file->path, &file->parentcluster)) {
      _free_file (ctx, file);
      return NULL;
    }
  }
  if (fatfs_get_file_entry (ctx, &ctx->_fs, file->parentcluster, file->filename, &sfEntry) == 1) {
    _free_file (ctx, file);
    return NULL;
  }
  file->startcluster = 0;
  if (!fatfs_allocate_free_space (ctx, &ctx->_fs, 1, &file->startcluster, 1)) {
    _free_file (ctx, file);
    return NULL;
  }
  tailNum = 0;
  do {
    fatfs_lfn_create_sfn (shortFilename, file->filename);
    if (tailNum != 0)
      fatfs_lfn_generate_tail ((char*)file->shortfilename, shortFilename, tailNum);
    else
      memcpy (file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
    if (fatfs_sfn_exists (ctx, &ctx->_fs, file->parentcluster, (char*)file->shortfilename) == 0)
      break;
    tailNum++;
  } while (tailNum < 9999);
  if (tailNum == 9999) {
    fatfs_free_cluster_chain (ctx, &ctx->_fs, file->startcluster);
    _free_file (ctx, file);
    return NULL;
  }
  if (!fatfs_add_file_entry (ctx, &ctx->_fs, file->parentcluster, (char*)file->filename,
                             (char*)file->shortfilename, file->startcluster, 0, 0)) {
    fatfs_free_cluster_chain (ctx, &ctx->_fs, file->startcluster);
    _free_file (ctx, file);
    return NULL;
  }
  file->filelength = 0;
  file->bytenum = 0;
  file->file_data_address = 0xFFFFFFFF;
  file->file_data_dirty = 0;
  file->filelength_changed = 0;
  file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
  file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
  fatfs_cache_init (&ctx->_fs, file);
  fatfs_fat_purge (ctx, &ctx->_fs);
  return file;
}
static uint32 _read_sectors (struct fatfs_ctx* ctx, FL_FILE* file, uint32 offset, uint8* buffer,
                             uint32 count) {
  uint32 Sector = 0;
  uint32 ClusterIdx = 0;
  uint32 Cluster = 0;
  uint32 i;
  uint32 lba;
  ClusterIdx = offset / ctx->_fs.sectors_per_cluster;
  Sector = offset - (ClusterIdx * ctx->_fs.sectors_per_cluster);
  if ((Sector + count) > ctx->_fs.sectors_per_cluster)
    count = ctx->_fs.sectors_per_cluster - Sector;
  if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
    Cluster = file->last_fat_lookup.CurrentCluster;
  else {
    if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1) {
      i = file->last_fat_lookup.ClusterIdx;
      Cluster = file->last_fat_lookup.CurrentCluster;
    } else {
      i = 0;
      Cluster = file->startcluster;
    }
    for (; i < ClusterIdx; i++) {
      uint32 nextCluster;
      if (!fatfs_cache_get_next_cluster (&ctx->_fs, file, i, &nextCluster)) {
        nextCluster = fatfs_find_next_cluster (ctx, &ctx->_fs, Cluster);
        fatfs_cache_set_next_cluster (&ctx->_fs, file, i, nextCluster);
      }
      Cluster = nextCluster;
    }
    if (Cluster != FAT32_LAST_CLUSTER) {
      file->last_fat_lookup.CurrentCluster = Cluster;
      file->last_fat_lookup.ClusterIdx = ClusterIdx;
    }
  }
  if (Cluster == FAT32_LAST_CLUSTER)
    return 0;
  lba = fatfs_lba_of_cluster (&ctx->_fs, Cluster) + Sector;
  if (fatfs_sector_read (ctx, &ctx->_fs, lba, buffer, count))
    return count;
  else
    return 0;
}
void fl_init (struct fatfs_ctx* ctx) {
  int i;
  fat_list_init (&ctx->_free_file_list);
  fat_list_init (&ctx->_open_file_list);
  for (i = 0; i < FATFS_MAX_OPEN_FILES; i++)
    fat_list_insert_last (&ctx->_free_file_list, &ctx->_files[i].list_node);
  ctx->_filelib_init = 1;
}
void fl_attach_locks (struct fatfs_ctx* ctx, void (*lock) (void), void (*unlock) (void)) {
  ctx->_fs.fl_lock = lock;
  ctx->_fs.fl_unlock = unlock;
}
int fl_attach_media (struct fatfs_ctx* ctx, fn_diskio_read rd, fn_diskio_write wr) {
  int res;
  CHECK_FL_INIT (ctx);
  ctx->_fs.disk_io.read_media = rd;
  ctx->_fs.disk_io.write_media = wr;
  if ((res = fatfs_init (ctx, &ctx->_fs)) != FAT_INIT_OK) {
    FAT_PRINTF (("FAT_FS: Error could not load FAT details (%d)!\r\n", res));
    return res;
  }
  ctx->_filelib_valid = 1;
  return FAT_INIT_OK;
}
void fl_shutdown (struct fatfs_ctx* ctx) {
  CHECK_FL_INIT (ctx);
  FL_LOCK (&ctx->_fs);
  fatfs_fat_purge (ctx, &ctx->_fs);
  FL_UNLOCK (&ctx->_fs);
}
void* fl_fopen (struct fatfs_ctx* ctx, const char* path, const char* mode) {
  int i;
  FL_FILE* file;
  uint8 flags = 0;
  CHECK_FL_INIT (ctx);
  if (!ctx->_filelib_valid)
    return NULL;
  if (!path || !mode)
    return NULL;
  for (i = 0; i < (int)strlen (mode); i++) {
    switch (mode[i]) {
    case 'r':
    case 'R':
      flags |= FILE_READ;
      break;
    case 'w':
    case 'W':
      flags |= FILE_WRITE;
      flags |= FILE_ERASE;
      flags |= FILE_CREATE;
      break;
    case 'a':
    case 'A':
      flags |= FILE_WRITE;
      flags |= FILE_APPEND;
      flags |= FILE_CREATE;
      break;
    case '+':
      if (flags & FILE_READ)
        flags |= FILE_WRITE;
      else if (flags & FILE_WRITE) {
        flags |= FILE_READ;
        flags |= FILE_ERASE;
        flags |= FILE_CREATE;
      } else if (flags & FILE_APPEND) {
        flags |= FILE_READ;
        flags |= FILE_WRITE;
        flags |= FILE_APPEND;
        flags |= FILE_CREATE;
      }
      break;
    case 'b':
    case 'B':
      flags |= FILE_BINARY;
      break;
    }
  }
  file = NULL;
  if (!ctx->_fs.disk_io.write_media)
    flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
  FL_LOCK (&ctx->_fs);
  if (flags & FILE_READ)
    file = _open_file (ctx, path);
  if (!file && (flags & FILE_CREATE))
    file = _create_file (ctx, path);
  if (!(flags & FILE_READ))
    if ((flags & FILE_CREATE) && !file)
      if (flags & (FILE_WRITE | FILE_APPEND))
        file = _open_file (ctx, path);
  if (file)
    file->flags = flags;
  FL_UNLOCK (&ctx->_fs);
  return file;
}
static uint32 _write_sectors (struct fatfs_ctx* ctx, FL_FILE* file, uint32 offset, uint8* buf,
                              uint32 count) {
  uint32 SectorNumber = 0;
  uint32 ClusterIdx = 0;
  uint32 Cluster = 0;
  uint32 LastCluster = FAT32_LAST_CLUSTER;
  uint32 i;
  uint32 lba;
  uint32 TotalWriteCount = count;
  ClusterIdx = offset / ctx->_fs.sectors_per_cluster;
  SectorNumber = offset - (ClusterIdx * ctx->_fs.sectors_per_cluster);
  if ((SectorNumber + count) > ctx->_fs.sectors_per_cluster)
    count = ctx->_fs.sectors_per_cluster - SectorNumber;
  if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
    Cluster = file->last_fat_lookup.CurrentCluster;
  else {
    if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1) {
      i = file->last_fat_lookup.ClusterIdx;
      Cluster = file->last_fat_lookup.CurrentCluster;
    } else {
      i = 0;
      Cluster = file->startcluster;
    }
    for (; i < ClusterIdx; i++) {
      uint32 nextCluster;
      if (!fatfs_cache_get_next_cluster (&ctx->_fs, file, i, &nextCluster)) {
        nextCluster = fatfs_find_next_cluster (ctx, &ctx->_fs, Cluster);
        fatfs_cache_set_next_cluster (&ctx->_fs, file, i, nextCluster);
      }
      LastCluster = Cluster;
      Cluster = nextCluster;
      if (Cluster == FAT32_LAST_CLUSTER)
        break;
    }
    if (Cluster == FAT32_LAST_CLUSTER) {
      if (!fatfs_add_free_space (ctx, &ctx->_fs, &LastCluster,
                                 (TotalWriteCount + ctx->_fs.sectors_per_cluster - 1) /
                                     ctx->_fs.sectors_per_cluster))
        return 0;
      Cluster = LastCluster;
    }
    file->last_fat_lookup.CurrentCluster = Cluster;
    file->last_fat_lookup.ClusterIdx = ClusterIdx;
  }
  lba = fatfs_lba_of_cluster (&ctx->_fs, Cluster) + SectorNumber;
  if (fatfs_sector_write (ctx, &ctx->_fs, lba, buf, count))
    return count;
  else
    return 0;
}
int fl_fflush (struct fatfs_ctx* ctx, void* f) {
  FL_FILE* file = (FL_FILE*)f;
  CHECK_FL_INIT (ctx);
  if (file) {
    FL_LOCK (&ctx->_fs);
    if (file->file_data_dirty) {
      if (_write_sectors (ctx, file, file->file_data_address, file->file_data_sector, 1))
        file->file_data_dirty = 0;
    }
    FL_UNLOCK (&ctx->_fs);
  }
  return 0;
}
void fl_fclose (struct fatfs_ctx* ctx, void* f) {
  FL_FILE* file = (FL_FILE*)f;
  CHECK_FL_INIT (ctx);
  if (file) {
    FL_LOCK (&ctx->_fs);
    fl_fflush (ctx, f);
    if (file->filelength_changed) {
      fatfs_update_file_length (ctx, &ctx->_fs, file->parentcluster, (char*)file->shortfilename,
                                file->filelength);
      file->filelength_changed = 0;
    }
    file->bytenum = 0;
    file->filelength = 0;
    file->startcluster = 0;
    file->file_data_address = 0xFFFFFFFF;
    file->file_data_dirty = 0;
    file->filelength_changed = 0;
    _free_file (ctx, file);
    fatfs_fat_purge (ctx, &ctx->_fs);
    FL_UNLOCK (&ctx->_fs);
  }
}
int fl_fgetc (struct fatfs_ctx* ctx, void* f) {
  int res;
  uint8 data = 0;
  res = fl_fread (ctx, &data, 1, 1, f);
  if (res == 1)
    return (int)data;
  else
    return res;
}
char* fl_fgets (struct fatfs_ctx* ctx, char* s, int n, void* f) {
  int idx = 0;
  if (n > 0) {
    while (idx < (n - 1)) {
      int ch = fl_fgetc (ctx, f);
      if (ch < 0)
        break;
      s[idx++] = (char)ch;
      if (ch == '\n')
        break;
    }
    if (idx > 0)
      s[idx] = '\0';
  }
  return (idx > 0) ? s : 0;
}
int fl_fread (struct fatfs_ctx* ctx, void* buffer, int size, int length, void* f) {
  uint32 sector;
  uint32 offset;
  int copyCount;
  int count = size * length;
  int bytesRead = 0;
  FL_FILE* file = (FL_FILE*)f;
  CHECK_FL_INIT (ctx);
  if (buffer == NULL || file == NULL)
    return -1;
  if (!(file->flags & FILE_READ))
    return -1;
  if (!count)
    return 0;
  if (file->bytenum >= file->filelength)
    return -1;
  if ((file->bytenum + count) > file->filelength)
    count = file->filelength - file->bytenum;
  sector = file->bytenum / FAT_SECTOR_SIZE;
  offset = file->bytenum % FAT_SECTOR_SIZE;
  while (bytesRead < count) {
    if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE)) {
      uint32 sectorsRead = _read_sectors (ctx, file, sector, (uint8*)((uint8*)buffer + bytesRead),
                                          (count - bytesRead) / FAT_SECTOR_SIZE);
      if (sectorsRead) {
        copyCount = FAT_SECTOR_SIZE * sectorsRead;
        sector += sectorsRead;
        offset = 0;
      } else
        break;
    } else {
      if (file->file_data_address != sector) {
        if (file->file_data_dirty)
          fl_fflush (ctx, file);
        if (!_read_sectors (ctx, file, sector, file->file_data_sector, 1))
          break;
        file->file_data_address = sector;
        file->file_data_dirty = 0;
      }
      copyCount = FAT_SECTOR_SIZE - offset;
      if (copyCount > (count - bytesRead))
        copyCount = (count - bytesRead);
      memcpy ((uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset),
              copyCount);
      sector++;
      offset = 0;
    }
    bytesRead += copyCount;
    file->bytenum += copyCount;
  }
  return bytesRead;
}
int fl_fseek (struct fatfs_ctx* ctx, void* f, long offset, int origin) {
  FL_FILE* file = (FL_FILE*)f;
  int res = -1;
  CHECK_FL_INIT (ctx);
  if (!file)
    return -1;
  if (origin == SEEK_END && offset != 0)
    return -1;
  FL_LOCK (&ctx->_fs);
  file->file_data_address = 0xFFFFFFFF;
  file->file_data_dirty = 0;
  if (origin == SEEK_SET) {
    file->bytenum = (uint32)offset;
    if (file->bytenum > file->filelength)
      file->bytenum = file->filelength;
    res = 0;
  } else if (origin == SEEK_CUR) {
    if (offset >= 0) {
      file->bytenum += offset;
      if (file->bytenum > file->filelength)
        file->bytenum = file->filelength;
    } else {
      offset = -offset;
      if ((uint32)offset > file->bytenum)
        file->bytenum = 0;
      else
        file->bytenum -= offset;
    }
    res = 0;
  } else if (origin == SEEK_END) {
    file->bytenum = file->filelength;
    res = 0;
  } else
    res = -1;
  FL_UNLOCK (&ctx->_fs);
  return res;
}
int fl_fgetpos (struct fatfs_ctx* ctx, void* f, uint32* position) {
  FL_FILE* file = (FL_FILE*)f;
  if (!file)
    return -1;
  FL_LOCK (&ctx->_fs);
  *position = file->bytenum;
  FL_UNLOCK (&ctx->_fs);
  return 0;
}
long fl_ftell (struct fatfs_ctx* ctx, void* f) {
  uint32 pos = 0;
  fl_fgetpos (ctx, f, &pos);
  return (long)pos;
}
int fl_feof (struct fatfs_ctx* ctx, void* f) {
  FL_FILE* file = (FL_FILE*)f;
  int res;
  if (!file)
    return -1;
  FL_LOCK (&ctx->_fs);
  if (file->bytenum == file->filelength)
    res = EOF;
  else
    res = 0;
  FL_UNLOCK (&ctx->_fs);
  return res;
}
int fl_fputc (struct fatfs_ctx* ctx, int c, void* f) {
  uint8 data = (uint8)c;
  int res;
  res = fl_fwrite (ctx, &data, 1, 1, f);
  if (res == 1)
    return c;
  else
    return res;
}
int fl_fwrite (struct fatfs_ctx* ctx, const void* data, int size, int count, void* f) {
  FL_FILE* file = (FL_FILE*)f;
  uint32 sector;
  uint32 offset;
  uint32 length = (size * count);
  uint8* buffer = (uint8*)data;
  uint32 bytesWritten = 0;
  uint32 copyCount;
  CHECK_FL_INIT (ctx);
  if (!file)
    return -1;
  FL_LOCK (&ctx->_fs);
  if (!(file->flags & FILE_WRITE)) {
    FL_UNLOCK (&ctx->_fs);
    return -1;
  }
  if (file->flags & FILE_APPEND)
    file->bytenum = file->filelength;
  sector = file->bytenum / FAT_SECTOR_SIZE;
  offset = file->bytenum % FAT_SECTOR_SIZE;
  while (bytesWritten < length) {
    if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE)) {
      uint32 sectorsWrote;
      if (file->file_data_address != 0xFFFFFFFF) {
        if (file->file_data_dirty)
          fl_fflush (ctx, file);
        file->file_data_address = 0xFFFFFFFF;
        file->file_data_dirty = 0;
      }
      sectorsWrote = _write_sectors (ctx, file, sector, (uint8*)(buffer + bytesWritten),
                                     (length - bytesWritten) / FAT_SECTOR_SIZE);
      copyCount = FAT_SECTOR_SIZE * sectorsWrote;
      bytesWritten += copyCount;
      file->bytenum += copyCount;
      sector += sectorsWrote;
      offset = 0;
      if (!sectorsWrote)
        break;
    } else {
      copyCount = FAT_SECTOR_SIZE - offset;
      if (copyCount > (length - bytesWritten))
        copyCount = (length - bytesWritten);
      if (file->file_data_address != sector) {
        if (file->file_data_dirty)
          fl_fflush (ctx, file);
        if (copyCount != FAT_SECTOR_SIZE) {
          if (!_read_sectors (ctx, file, sector, file->file_data_sector, 1))
            memset (file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
        }
        file->file_data_address = sector;
        file->file_data_dirty = 0;
      }
      memcpy ((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten),
              copyCount);
      file->file_data_dirty = 1;
      bytesWritten += copyCount;
      file->bytenum += copyCount;
      sector++;
      offset = 0;
    }
  }
  if (file->bytenum > file->filelength) {
    file->filelength = file->bytenum;
    file->filelength_changed = 1;
  }
  FL_UNLOCK (&ctx->_fs);
  return (size * count);
}
int fl_fputs (struct fatfs_ctx* ctx, const char* str, void* f) {
  int len = (int)strlen (str);
  int res = fl_fwrite (ctx, str, 1, len, f);
  if (res == len)
    return len;
  else
    return res;
}
int fl_remove (struct fatfs_ctx* ctx, const char* filename) {
  FL_FILE* file;
  int res = -1;
  FL_LOCK (&ctx->_fs);
  file = (FL_FILE*)fl_fopen (ctx, (char*)filename, "r");
  if (file) {
    if (fatfs_free_cluster_chain (ctx, &ctx->_fs, file->startcluster)) {
      if (fatfs_mark_file_deleted (ctx, &ctx->_fs, file->parentcluster,
                                   (char*)file->shortfilename)) {
        fl_fclose (ctx, file);
        res = 0;
      }
    }
  }
  FL_UNLOCK (&ctx->_fs);
  return res;
}
int fl_createdirectory (struct fatfs_ctx* ctx, const char* path) {
  int res;
  CHECK_FL_INIT (ctx);
  FL_LOCK (&ctx->_fs);
  res = _create_directory (ctx, (char*)path);
  FL_UNLOCK (&ctx->_fs);
  return res;
}
void fl_listdirectory (struct fatfs_ctx* ctx, const char* path) {
  FL_DIR dirstat;
  CHECK_FL_INIT (ctx);
  FL_LOCK (&ctx->_fs);
  FAT_PRINTF (("\r\nDirectory %s\r\n", path));
  if (fl_opendir (ctx, path, &dirstat)) {
    struct fs_dir_ent dirent;
    while (fl_readdir (ctx, &dirstat, &dirent) == 0) {
      if (dirent.is_dir) {
        FAT_PRINTF (("%s <DIR>\r\n", dirent.filename));
      } else {
        FAT_PRINTF (("%s [%d bytes]\r\n", dirent.filename, dirent.size));
      }
    }
    fl_closedir (ctx, &dirstat);
  }
  FL_UNLOCK (&ctx->_fs);
}
FL_DIR* fl_opendir (struct fatfs_ctx* ctx, const char* path, FL_DIR* dir) {
  int levels;
  int res = 1;
  uint32 cluster = FAT32_INVALID_CLUSTER;
  CHECK_FL_INIT (ctx);
  FL_LOCK (&ctx->_fs);
  levels = fatfs_total_path_levels ((char*)path) + 1;
  if (levels == 0)
    cluster = fatfs_get_root_cluster (&ctx->_fs);
  else
    res = _open_directory (ctx, (char*)path, &cluster);
  if (res)
    fatfs_list_directory_start (&ctx->_fs, dir, cluster);
  FL_UNLOCK (&ctx->_fs);
  return cluster != FAT32_INVALID_CLUSTER ? dir : 0;
}
int fl_readdir (struct fatfs_ctx* ctx, FL_DIR* dirls, fl_dirent* entry) {
  int res = 0;
  CHECK_FL_INIT (ctx);
  FL_LOCK (&ctx->_fs);
  res = fatfs_list_directory_next (ctx, &ctx->_fs, dirls, entry);
  FL_UNLOCK (&ctx->_fs);
  return res ? 0 : -1;
}
int fl_closedir (struct fatfs_ctx* ctx, FL_DIR* dir) { return 0; }
int fl_is_dir (struct fatfs_ctx* ctx, const char* path) {
  int res = 0;
  FL_DIR dir;
  if (fl_opendir (ctx, path, &dir)) {
    res = 1;
    fl_closedir (ctx, &dir);
  }
  return res;
}
int fl_format (struct fatfs_ctx* ctx, uint32 volume_sectors, const char* name) {
  return fatfs_format (ctx, &ctx->_fs, volume_sectors, name);
}
#pragma clang diagnostic pop
